This app comes from Prisma's REST API Example and shows how to create a REST API using Express and Prisma Client and deploy it onto Unikraft Cloud. It uses a SQLite database file with some initial migration data.
To run this example, follow these steps:
-
Install the CLI. Use the unikraft CLI or the legacy kraft CLI. You need a BuildKit builder. The easiest way to get one is via Docker. Alternatively, you can also directly set up and use BuildKit, see the quick start.
Note: The unikraft CLI is the current standard, while kraft is the legacy version. Choose one of the CLIs below and only run the commands associated with it for the rest of this guide.
-
Clone the
examplesrepository andcdinto theexamples/httpserver-prisma-expressjs4.19-node18/directory:git clone https://github.com/unikraft-cloud/examples cd examples/httpserver-prisma-expressjs4.19-node18/
Make sure to log into Unikraft Cloud and pick a metro close to you.
This guide uses fra (Frankfurt, π©πͺ):
Using the unikraft CLI (Recommended)
unikraft loginor
Using the legacy kraft CLI
# Set Unikraft Cloud access token
export UKC_TOKEN=token
# Set metro to Frankfurt, DE
export UKC_METRO=fraWhen done, invoke the following command to deploy this app on Unikraft Cloud:
Using the unikraft CLI (Recommended)
unikraft build . --output <my-org>/httpserver-prisma-expressjs419-node18:latest
unikraft run --metro fra \
-m 512M \
-p 443:3000/tls+http \
--scale-to-zero policy=on,cooldown-time=1000 \
--image <my-org>/httpserver-prisma-expressjs419-node18:latestor
Using the legacy kraft CLI
kraft cloud deploy \
-M 512Mi \
-p 443:3000/tls+http \
--scale-to-zero on \
--scale-to-zero-cooldown 1s \
.The output shows the instance address and other details:
Using the unikraft CLI (Recommended)
metro: fra
name: httpserver-prisma-expressjs419-node18-hdof1
uuid: 066f55cb-bcbd-45e5-9f6b-b3866c3a3a4c
state: starting
image: <my-org>/httpserver-prisma-expressjs419-node18
resources:
memory: 512MiB
vcpus: 1
service:
uuid: b7da8a3b-ca4d-979a-3ae9-9634bca98008
name: funky-sun-4bf8v7g9
domains:
- fqdn: funky-sun-4bf8v7g9.fra.unikraft.app
networks:
- uuid: 63d977e8-548c-f0af-cc97-39856660f612
private-ip: 10.0.28.2
mac: 12:b0:43:fb:5c:30
timestamps:
created: just now
or
Using the legacy kraft CLI
[β] Deployed successfully!
β
ββββββββββ name: httpserver-prisma-expressjs419-node18-hdof1
ββββββββββ uuid: 066f55cb-bcbd-45e5-9f6b-b3866c3a3a4c
βββββββββ metro: https://api.fra.unikraft.cloud/v1
βββββββββ state: starting
ββββββββ domain: https://funky-sun-4bf8v7g9.fra.unikraft.app
βββββββββ image: oci://unikraft.io/<my-org>/httpserver-prisma-expressjs419-node18@sha256:770d4af1d490daea11171c680eaf99e2a6017a262ba9fbf1ba8d708f5fc32bfe
ββββββββ memory: 512 MiB
βββββββ service: funky-sun-4bf8v7g9
ββ private fqdn: httpserver-prisma-expressjs419-node18-hdof1.internal
ββββ private ip: 10.0.28.2
In this case, the instance name is httpserver-prisma-expressjs419-node18-hdof1 and the address is https://funky-sun-4bf8v7g9.fra.unikraft.app.
They're different for each run.
Use curl to test the REST API, such as the /users endpoint:
curl https://funky-sun-4bf8v7g9.fra.unikraft.app/users[{"id":1,"email":"alice@prisma.io","name":"Alice"},
{"id":2,"email":"nilu@prisma.io","name":"Nilu"},
{"id":3,"email":"mahmoud@prisma.io","name":"Mahmoud"}]You can list information about the instance by running:
Using the unikraft CLI (Recommended)
unikraft instances listMETRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED
fra httpserver-prisma-expressjs419-node18-hdof1 running <my-org>/httpserver-prisma-expressjs419-node18 512MiB 1 funky-sun-4bf8v7g9.fra.unikraft.app 2 minutes ago
or
Using the legacy kraft CLI
kraft cloud instance listNAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME
httpserver-prisma-expressjs419-node18-hdof1 funky-sun-4bf8v7g9.fra.unikraft.app running 1 minute ago oci://unikraft.io/<my-org>/httpserver-prisma-expressjs419-node18@sha256:... 512 MiB 1 37.94 ms
When done, you can remove the instance:
Using the unikraft CLI (Recommended)
unikraft instances delete httpserver-prisma-expressjs419-node18-hdof1or
Using the legacy kraft CLI
kraft cloud instance remove httpserver-prisma-expressjs419-node18-hdof1You can access the REST API of the server using the following endpoints:
GET /post/:id: Fetch a single post by itsidGET /feed?searchString={searchString}&take={take}&skip={skip}&orderBy={orderBy}: Fetch all published posts- Query Parameters
searchString(optional): This filters posts bytitleorcontenttake(optional): This specifies how many objects the list should returnskip(optional): This specifies how many of the returned objects in the list to skiporderBy(optional): The sort order for posts in either ascending or descending order. The value can eitherascordesc
- Query Parameters
GET /user/:id/drafts: Fetch user's drafts by theiridGET /users: Fetch all usersPOST /post: Create a new post- Body:
title: String(required): The title of the postcontent: String(optional): The content of the postauthorEmail: String(required): The email of the user that creates the post
- Body:
POST /signup: Create a new user- Body:
email: String(required): The email address of the username: String(optional): The name of the userpostData: PostCreateInput[](optional): The posts of the user
- Body:
PUT /publish/:id: Toggle the publish value of a post by itsidPUT /post/:id/views: Increases theviewCountof aPostby oneidDELETE /post/:id: Delete a post by itsid
Evolving the app typically requires two steps:
- Migrate your database using Prisma Migrate
- Update your app code
For the following example scenario, assume you want to add a "profile" feature to the app where users can create a profile and write a short bio about themselves.
The first step is to add a new table, for example called Profile, to the database.
You can do this by adding a new model to your Prisma schema file file and then running a migration afterward:
// ./prisma/schema.prisma
model User {
id Int @default(autoincrement()) @id
name String?
email String @unique
posts Post[]
+ profile Profile?
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String?
published Boolean @default(false)
viewCount Int @default(0)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
+model Profile {
+ id Int @default(autoincrement()) @id
+ bio String?
+ user User @relation(fields: [userId], references: [id])
+ userId Int @unique
+}Once you've updated your data model, you can execute the changes against your database with the following command:
npx prisma migrate dev --name add-profileThis adds another migration to the prisma/migrations directory and creates the new Profile table in the database.
You can now use your PrismaClient instance to perform operations against the new Profile table.
Those operations can create API endpoints in the REST API.
Update your src/index.js file by adding a new endpoint to your API:
app.post('/user/:id/profile', async (req, res) => {
const { id } = req.params
const { bio } = req.body
const profile = await prisma.profile.create({
data: {
bio,
user: {
connect: {
id: Number(id)
}
}
}
})
res.send(profile)
})Restart your app server and test out your new endpoint.
/user/:id/profile: Create a new profile based on the user id- Body:
bio: String: The bio of the user
- Body:
Here are some more sample Prisma Client queries on the new Profile model:
const profile = await prisma.profile.create({
data: {
bio: 'Hello World',
user: {
connect: { email: 'alice@prisma.io' },
},
},
})const user = await prisma.user.create({
data: {
email: 'john@prisma.io',
name: 'John',
profile: {
create: {
bio: 'Hello World',
},
},
},
})const userWithUpdatedProfile = await prisma.user.update({
where: { email: 'alice@prisma.io' },
data: {
profile: {
update: {
bio: 'Hello Friends',
},
},
},
})If you want to try this example with another database than SQLite, you can adjust the database connection in prisma/schema.prisma by reconfiguring the datasource block.
Learn more about the different connection configurations in the docs.
For PostgreSQL, the connection address has the following structure:
datasource db {
provider = "postgresql"
url = "postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA"
}Here is an example connection string with a local PostgreSQL database:
datasource db {
provider = "postgresql"
url = "postgresql://janedoe:mypassword@localhost:5432/notesapi?schema=public"
}For MySQL, the connection address has the following structure:
datasource db {
provider = "mysql"
url = "mysql://USER:PASSWORD@HOST:PORT/DATABASE"
}Here is an example connection string with a local MySQL database:
datasource db {
provider = "mysql"
url = "mysql://janedoe:mypassword@localhost:3306/notesapi"
}Here is an example connection string with a local Microsoft SQL Server database:
datasource db {
provider = "sqlserver"
url = "sqlserver://localhost:1433;initial catalog=sample;user=sa;password=mypassword;"
}Here is an example connection string with a local MongoDB database:
datasource db {
provider = "mongodb"
url = "mongodb://USERNAME:PASSWORD@HOST/DATABASE?authSource=admin&retryWrites=true&w=majority"
}Use the --help option for detailed information on using Unikraft Cloud:
Using the unikraft CLI (Recommended)
unikraft --helpor
Using the legacy kraft CLI
kraft cloud --helpOr visit the CLI Reference or the legacy CLI Reference.