|
| 1 | +# Fullstack Example with Analog (REST API) |
| 2 | + |
| 3 | +This example shows how to implement a **fullstack app with [Analog](https://analogjs.org/)** using [Prisma Client](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client). It uses a SQLite database file with some initial dummy data which you can find at [`./prisma/dev.db`](./prisma/dev.db). |
| 4 | + |
| 5 | +## Getting started |
| 6 | + |
| 7 | +### 1. Download example and install dependencies |
| 8 | + |
| 9 | +Download this example: |
| 10 | + |
| 11 | +``` |
| 12 | +npx try-prisma@latest --template typescript/rest-analog |
| 13 | +``` |
| 14 | + |
| 15 | +Install npm dependencies: |
| 16 | + |
| 17 | +``` |
| 18 | +cd rest-analog |
| 19 | +npm install |
| 20 | +``` |
| 21 | + |
| 22 | +<details><summary><strong>Alternative:</strong> Clone the entire repo</summary> |
| 23 | + |
| 24 | +Clone this repository: |
| 25 | + |
| 26 | +``` |
| 27 | +git clone [email protected]:prisma/prisma-examples.git --depth=1 |
| 28 | +``` |
| 29 | + |
| 30 | +Install npm dependencies: |
| 31 | + |
| 32 | +``` |
| 33 | +cd prisma-examples/typescript/rest-analog |
| 34 | +npm install |
| 35 | +``` |
| 36 | + |
| 37 | +</details> |
| 38 | + |
| 39 | +### 2. Create and seed the database |
| 40 | + |
| 41 | +Run the following command to create your SQLite database file. This also creates the `User` and `Post` tables that are defined in [`prisma/schema.prisma`](./prisma/schema.prisma): |
| 42 | + |
| 43 | +``` |
| 44 | +npx prisma migrate dev --name init |
| 45 | +``` |
| 46 | + |
| 47 | +When `npx prisma migrate dev` is executed against a newly created database, seeding is also triggered. The seed file in [`prisma/seed.ts`](./prisma/seed.ts) will be executed and your database will be populated with the sample data. |
| 48 | + |
| 49 | +### 3. Start the app |
| 50 | + |
| 51 | +``` |
| 52 | +npm run dev |
| 53 | +``` |
| 54 | + |
| 55 | +The app is now running, navigate to [`http://localhost:5173/`](http://localhost:5173/) in your browser to explore its UI. |
| 56 | + |
| 57 | +<details><summary>Expand for a tour through the UI of the app</summary> |
| 58 | + |
| 59 | +<br /> |
| 60 | + |
| 61 | +**Blog** (located in [`./src/app/pages/index.page.ts`](./src/app/pages/index.page.ts) |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | +**Signup** (located in [`./rc/app/pages/signup.page.ts`](./src/app/pages/signup.page.ts)) |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +**Create post (draft)** (located in [`./src/app/pages/create.page.ts`](./src/app/pages/create.page.ts)) |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +**Drafts** (located in [`./src/app/pages/drafts.page.ts`](./src/app/pages/drafts.page.ts)) |
| 74 | + |
| 75 | + |
| 76 | + |
| 77 | +**View post** (located in [`./src/app/pages/p/_id.vue`](src/app/pages/post/_id.vue)) (delete or publish here) |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | +</details> |
| 82 | + |
| 83 | +## Using the REST API |
| 84 | + |
| 85 | +You can also access the REST API of the API server directly. It is running on the same host machine and port and can be accessed via the `/api` route (in this case that is `localhost:3000/api/`, so you can e.g. reach the API with [`localhost:3000/api/feed`](http://localhost:3000/api/feed)). |
| 86 | + |
| 87 | +### `GET` |
| 88 | + |
| 89 | +- `/api/post/:id`: Fetch a single post by its `id` |
| 90 | +- `/api/feed`: Fetch all _published_ posts |
| 91 | +- `/api/filterPosts?searchString={searchString}`: Filter posts by `title` or `content` |
| 92 | + |
| 93 | +### `POST` |
| 94 | + |
| 95 | +- `/api/post`: Create a new post |
| 96 | + - Body: |
| 97 | + - `title: String` (required): The title of the post |
| 98 | + - `content: String` (optional): The content of the post |
| 99 | + - `authorEmail: String` (required): The email of the user that creates the post |
| 100 | +- `/api/user`: Create a new user |
| 101 | + - Body: |
| 102 | + - `email: String` (required): The email address of the user |
| 103 | + - `name: String` (optional): The name of the user |
| 104 | + |
| 105 | +### `PUT` |
| 106 | + |
| 107 | +- `/api/publish/:id`: Publish a post by its `id` |
| 108 | + |
| 109 | +### `DELETE` |
| 110 | + |
| 111 | +- `/api/post/:id`: Delete a post by its `id` |
| 112 | + |
| 113 | +## Switch to another database (e.g. PostgreSQL, MySQL, SQL Server, MongoDB) |
| 114 | + |
| 115 | +If you want to try this example with another database than SQLite, you can adjust the the database connection in [`prisma/schema.prisma`](./prisma/schema.prisma) by reconfiguring the `datasource` block. |
| 116 | + |
| 117 | +Learn more about the different connection configurations in the [docs](https://www.prisma.io/docs/reference/database-reference/connection-urls). |
| 118 | + |
| 119 | +<details><summary>Expand for an overview of example configurations with different databases</summary> |
| 120 | + |
| 121 | +### PostgreSQL |
| 122 | + |
| 123 | +For PostgreSQL, the connection URL has the following structure: |
| 124 | + |
| 125 | +```prisma |
| 126 | +datasource db { |
| 127 | + provider = "postgresql" |
| 128 | + url = "postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA" |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +Here is an example connection string with a local PostgreSQL database: |
| 133 | + |
| 134 | +```prisma |
| 135 | +datasource db { |
| 136 | + provider = "postgresql" |
| 137 | + url = "postgresql://janedoe:mypassword@localhost:5432/notesapi?schema=public" |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +### MySQL |
| 142 | + |
| 143 | +For MySQL, the connection URL has the following structure: |
| 144 | + |
| 145 | +```prisma |
| 146 | +datasource db { |
| 147 | + provider = "mysql" |
| 148 | + url = "mysql://USER:PASSWORD@HOST:PORT/DATABASE" |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +Here is an example connection string with a local MySQL database: |
| 153 | + |
| 154 | +```prisma |
| 155 | +datasource db { |
| 156 | + provider = "mysql" |
| 157 | + url = "mysql://janedoe:mypassword@localhost:3306/notesapi" |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +### Microsoft SQL Server |
| 162 | + |
| 163 | +Here is an example connection string with a local Microsoft SQL Server database: |
| 164 | + |
| 165 | +```prisma |
| 166 | +datasource db { |
| 167 | + provider = "sqlserver" |
| 168 | + url = "sqlserver://localhost:1433;initial catalog=sample;user=sa;password=mypassword;" |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +### MongoDB |
| 173 | + |
| 174 | +Here is an example connection string with a local MongoDB database: |
| 175 | + |
| 176 | +```prisma |
| 177 | +datasource db { |
| 178 | + provider = "mongodb" |
| 179 | + url = "mongodb://USERNAME:PASSWORD@HOST/DATABASE?authSource=admin&retryWrites=true&w=majority" |
| 180 | +} |
| 181 | +``` |
| 182 | + |
| 183 | +</details> |
| 184 | + |
| 185 | +## Evolving the app |
| 186 | + |
| 187 | +Evolving the application typically requires three steps: |
| 188 | + |
| 189 | +1. Migrate your database using Prisma Migrate |
| 190 | +1. Update your server-side application code |
| 191 | +1. Build new UI features in Vue |
| 192 | + |
| 193 | +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. |
| 194 | + |
| 195 | +### 1. Migrate your database using Prisma Migrate |
| 196 | + |
| 197 | +The first step is to add a new table, e.g. called `Profile`, to the database. You can do this by adding a new model to your [Prisma schema file](./prisma/schema.prisma) file and then running a migration afterwards: |
| 198 | + |
| 199 | +```diff |
| 200 | +// schema.prisma |
| 201 | + |
| 202 | +model Post { |
| 203 | + id Int @default(autoincrement()) @id |
| 204 | + title String |
| 205 | + content String? |
| 206 | + published Boolean @default(false) |
| 207 | + author User? @relation(fields: [authorId], references: [id]) |
| 208 | + authorId Int |
| 209 | +} |
| 210 | + |
| 211 | +model User { |
| 212 | + id Int @default(autoincrement()) @id |
| 213 | + name String? |
| 214 | + email String @unique |
| 215 | + posts Post[] |
| 216 | ++ profile Profile? |
| 217 | +} |
| 218 | + |
| 219 | ++model Profile { |
| 220 | ++ id Int @default(autoincrement()) @id |
| 221 | ++ bio String? |
| 222 | ++ userId Int @unique |
| 223 | ++ user User @relation(fields: [userId], references: [id]) |
| 224 | ++} |
| 225 | +``` |
| 226 | + |
| 227 | +Once you've updated your data model, you can execute the changes against your database with the following command: |
| 228 | + |
| 229 | +``` |
| 230 | +npx prisma migrate dev --name add-profile |
| 231 | +``` |
| 232 | + |
| 233 | +### 2. Update your application code |
| 234 | + |
| 235 | +You can now use your `PrismaClient` instance to perform operations against the new `Profile` table. Here are some examples: |
| 236 | + |
| 237 | +#### Create a new profile for an existing user |
| 238 | + |
| 239 | +```ts |
| 240 | +const profile = await prisma.profile.create({ |
| 241 | + data: { |
| 242 | + bio: 'Hello World', |
| 243 | + user: { |
| 244 | + connect: { email: '[email protected]' }, |
| 245 | + }, |
| 246 | + }, |
| 247 | +}) |
| 248 | +``` |
| 249 | + |
| 250 | +#### Create a new user with a new profile |
| 251 | + |
| 252 | +```ts |
| 253 | +const user = await prisma.user.create({ |
| 254 | + data: { |
| 255 | + |
| 256 | + name: 'John', |
| 257 | + profile: { |
| 258 | + create: { |
| 259 | + bio: 'Hello World', |
| 260 | + }, |
| 261 | + }, |
| 262 | + }, |
| 263 | +}) |
| 264 | +``` |
| 265 | + |
| 266 | +#### Update the profile of an existing user |
| 267 | + |
| 268 | +```ts |
| 269 | +const userWithUpdatedProfile = await prisma.user.update({ |
| 270 | + where: { email: '[email protected]' }, |
| 271 | + data: { |
| 272 | + profile: { |
| 273 | + update: { |
| 274 | + bio: 'Hello Friends', |
| 275 | + }, |
| 276 | + }, |
| 277 | + }, |
| 278 | +}) |
| 279 | +``` |
| 280 | + |
| 281 | +### 5. Build new UI features in Vue |
| 282 | + |
| 283 | +Once you have added a new endpoint to the API (e.g. `/api/profile` with `/POST`, `/PUT` and `GET` operations), you can start building a new UI component in Vue. It could e.g. be called `profile.vue` and would be located in the `pages` directory. |
| 284 | + |
| 285 | +In the application code, you can access the new endpoint via `fetch` operations and populate the UI with the data you receive from the API calls. |
| 286 | + |
| 287 | +## Next steps |
| 288 | + |
| 289 | +- Check out the [Prisma docs](https://www.prisma.io/docs) |
| 290 | +- Share your feedback on the [Prisma Discord](https://pris.ly/discord/) |
| 291 | +- Create issues and ask questions on [GitHub](https://github.com/prisma/prisma/) |
0 commit comments