Quickstart
Tip
If you're want to quickly see what Contember can do, check out our Headless CMS template. It's the easiest way to try out what Contember can do in minutes.
Create new project with Contember
In this tutorial we will show you how to setup both Contember Engine and Contember Admin to create your first project.
Prerequisites
- Installed NPM version 7+
- Installed Docker with Docker Compose
npm exec "@contember/create@latest" quickstart
It will create a new folder with basic Contember setup. After it is done:
cd quickstart
And then install dependencies:
npm install
And you're ready to go. Just start Docker containers and Contember will be running.
npm start
Now, administration UI should be running on address http://localhost:1480.
Started services
- Admin UI at http://localhost:1480
- API endpoints at http://localhost:1481 (you can authorize with token
0000000000000000000000000000000000000000
)
For advanced use-cases there is also:
- Adminer database management tool at http://localhost:1485.
- Minio local S3 provider at http://localhost:1483 (you can sign in with contembeer / contember credentials).
- Mailhog testing SMTP at http://localhost:1484.
- PostgreSQL database at localhost:1482 (you can sign in with contember / contember credentials).
Create first project
From the first step you should have a folder structure that looks like this:
quickstart/
├── admin/
│ ├── components/
│ ├── pages/
│ ├── .env
│ ├── index.html
│ ├── index.tsx
│ └── vite-env.d.ts
├── api/
│ ├── migrations/
│ ├── model/
│ ├── acl.ts
│ └── index.ts
├── docker/
├── node_modules/
├── docker-compose.yaml
├── package.json
├── package-lock.json
└── tsconfig.json
Create your first data model
First you have to tell Contember Engine, how your data model looks like. The init
command automatically created api/model/index.ts
file, so go there.
Here you start defining your project schema. Really simple example looks like this:
import { SchemaDefinition as def } from '@contember/schema-definition'
export class Article {
title = def.stringColumn()
content = def.stringColumn()
}
- Import
SchemaDefinition
so you'll get TypeScript autocompletion. - Define your first entity -
Article
. For this example let's just add two columns namedtitle
andcontent
, both arestring
.
Then you need to generate a database migration for Contember Engine:
npm run contember migrations:diff quickstart add-article
Contember CLI
npm run contember
is a Contember CLI, if you call this without any arguments you'll see all the available commands.
We'll use migrations:diff
command. It goes through your schema and generates migration - instructions for Contember how to get from previous state to your new one.
This command needs two parameters: first is name of your project (quickstart
in our example) and then name your migration. It can be anything you want.
Run this command and choose an option Yes and execute immediately
. It will create your migration and after confirmation execute it. Now if you would look into your database, you would see there a table article
with three columns: id
, title
, content
. Nice.
Create your administration UI
Now we have something we want to edit in UI. Let's start by adding a listing page for our articles.
Add listing page
Go to admin/pages
and create new file articles.tsx
.
import * as React from 'react'
import { DataGridPage, TextCell } from '@contember/admin'
export const articleList = (
<DataGridPage entities="Article" rendererProps={{ title: 'Articles' }}>
<TextCell field="title" header="Title" />
</DataGridPage>
)
- Import
@contember/admin
package for TypeScript autocompletion. - Export
articleList
. The export name is used for linking to the page and in URL. - Use
DataGridPage
component to show the data in a simple datagrid. - Tell it which entities you'd like to edit. In our case it's
Article
(it has to be the same name we used in the model). - Use
TextCell
to add text column.
If you go to localhost:1480/article-list you'll see list of your articles. Which is empty as we didn't add any data there yet.
Let's add some data.
Add create page
import * as React from 'react'
import { CreatePage, DataGridPage, RichTextField, TextCell, TextField } from '@contember/admin'
export const articleList = (
<DataGridPage entities="Article" rendererProps={{ title: 'Articles' }}>
<TextCell field="title" header="Title" />
</DataGridPage>
)
export const articleCreate = (
<CreatePage entity="Article" rendererProps={{ title: 'Create Article' }}>
<TextField field="title" label="Title" />
<RichTextField field="content" label="Content" />
</CreatePage>
)
- For simplicity, we'll add it to the same file.
- This time we'll use
CreatePage
component exported asarticleCreate
- We'll tell it what we want to add (
Article
). - We'll use two new components -
TextField
andRichTextField
and tell them what fields to edit.
Now at localhost:1480/article-create you can create new article. And if you go to the list of articles you'll see the data are there.
But it doesn't work very well. One of the things missing is to go to edit mode after you created a new article. So let's start by adding an edit page:
Add edit page
For simplicity, we'll add it to the same file as well. It looks almost the same as the create page - but we have to tell it which article to edit:
import * as React from 'react'
import { CreatePage, DataGridPage, EditPage, RichTextField, TextCell, TextField } from '@contember/admin'
export const articleList = (
<DataGridPage entities="Article" rendererProps={{ title: 'Articles' }}>
<TextCell field="title" header="Title" />
</DataGridPage>
)
export const articleCreate = (
<CreatePage entity="Article" rendererProps={{ title: 'Create Article' }}>
<TextField field="title" label="Title" />
<RichTextField field="content" label="Content" />
</CreatePage>
)
export const articleEdit = (
<EditPage entity="Article(id = $id)" rendererProps={{ title: 'Edit Article' }}>
<TextField field="title" label="Title" />
<RichTextField field="content" label="Content" />
</EditPage>
)
Let's use it. We'll redirect users from our create page to the edit page after the article is successfully created:
export const articleCreate = (
<CreatePage
entity="Article"
rendererProps={{ title: 'Create Article' }}
redirectOnSuccess="articleEdit(id: $entity.id)"
>
<TextField field="title" label="Title" />
<RichTextField field="content" label="Content" />
</CreatePage>
)
This is done with redirectOnSuccess
prop where we specify link to page where user should be redirected.
Now if you create a new article you're automatically redirected to the edit page.
What's missing is an edit and delete button in the list of pages.
import * as React from 'react'
import {
CreatePage,
DataGridPage,
DeleteEntityButton,
EditPage,
GenericCell,
Link,
RichTextField,
TextCell,
TextField,
} from '@contember/admin'
export const articleList = (
<DataGridPage entities="Article" rendererProps={{ title: 'Articles' }}>
<TextCell field="title" header="Title" />
<GenericCell shrunk><Link to="articleEdit(id: $entity.id)">Edit</Link></GenericCell>
<GenericCell shrunk><DeleteEntityButton immediatePersist /></GenericCell>
</DataGridPage>
)
export const articleCreate = (
<CreatePage
entity="Article"
rendererProps={{ title: 'Create Article' }}
redirectOnSuccess="articleEdit(id: $entity.id)"
>
<TextField field="title" label="Title" />
<RichTextField field="content" label="Content" />
</CreatePage>
)
export const articleEdit = (
<EditPage entity="Article(id = $id)" rendererProps={{ title: 'Edit Article' }}>
<TextField field="title" label="Title" />
<RichTextField field="content" label="Content" />
</EditPage>
)
- We've added two
GenericCell
. - First with
Link
component targetingarticleEdit
page and passingid
as parameter. - Second with delete button provided by
DeleteEntityButton
. - Minor touch is use of
shrunk
with tells the cell to be as small as possible.
Add pages to side menu
One last thing is to add our pages to the left sidebar:
import * as React from 'react'
import { Menu } from '@contember/admin'
export const Navigation = () => (
<Menu>
<Menu.Item>
<Menu.Item title="Dashboard" to="index" />
<Menu.Item title="Articles" to="articleList" />
<Menu.Item title="Create new article" to="articleCreate" />
</Menu.Item>
</Menu>
)
And that's it! You have just created a simple data model and created custom interface, so you can edit the data.
Fetch data via GraphQL API
We recommend reading Content API topic however if you're looking to quickly play with the API, we've prepared Insomnia project for you to import and quickly try it out. To use it download it here and just drag&drop it to Insomnia.