Skip to content

Commit 1063ce3

Browse files
committed
first commit
0 parents  commit 1063ce3

21 files changed

+1115
-0
lines changed

.gitignore

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# This gitignore file is used only internally for this repository,
2+
# you should not use this in your own projects. Instead, navigate into
3+
# an example folder and use the folder as the project root (it also contains a
4+
# projects-specific .gitignore).
5+
6+
node_modules/
7+
yarn-error.log/
8+
vendor/
9+
tmp/
10+
dist/
11+
.DS_Store
12+
.idea
13+
tmp-resolvers
14+
package-lock.json
15+
flow-typed
16+
yarn.lock
17+
javascript/**/migrations/
18+
typescript/**/migrations/
19+
misc/**/migrations/
20+
databases/**/migrations/
21+
experimental/**/*.db
22+
.next
23+
.vscode/
24+
.nuxt
25+
dev.db
26+
*.env*

README.md

+267
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Your tasks
2+
3+
IMPORTANT: For all the tasks, focus on best practices. Aim for solutions that not only work but that you'd be happy to push to a production app.
4+
5+
## 1. Draft post bugfix
6+
7+
After running this app, you'll notice that the backend crashes if you try adding a new draft post with an email address that doesn't belong to any existing user. To reproduce this issue:
8+
9+
- Click "Create draft"
10+
- Enter a title
11+
- Enter an email address that doesn't belong to any user, for example '[email protected]'
12+
- Enter content
13+
- Click "Create"
14+
- You'll see the server crash with the following error message:
15+
16+
```
17+
/Users/polygon/Documents/development.nosync/recruitment-task/backend/node_modules/@prisma/client/runtime/index.js:45405
18+
throw new PrismaClientKnownRequestError(message, e.code, this.client._clientVersion, e.meta);
19+
^
20+
PrismaClientKnownRequestError:
21+
Invalid `prisma.post.create()` invocation in
22+
/Users/polygon/Documents/development.nosync/recruitment-task/backend/src/index.ts:50:36
23+
24+
47
25+
48 app.post(`/post`, async (req, res) => {
26+
49 const { title, content, authorEmail } = req.body
27+
→ 50 const result = await prisma.post.create(
28+
An operation failed because it depends on one or more records that were required but not found. No 'User' record(s) (needed to inline the relation on 'Post' record(s)) was found for a nested connect on one-to-many relation 'PostToUser'.
29+
at Object.request (/Users/polygon/Documents/development.nosync/recruitment-task/backend/node_modules/@prisma/client/runtime/index.js:45405:15)
30+
at async PrismaClient._request (/Users/polygon/Documents/development.nosync/recruitment-task/backend/node_modules/@prisma/client/runtime/index.js:46301:18) {
31+
code: 'P2025',
32+
clientVersion: '3.14.0',
33+
meta: {
34+
cause: "No 'User' record(s) (needed to inline the relation on 'Post' record(s)) was found for a nested connect on one-to-many relation 'PostToUser'."
35+
}
36+
}
37+
```
38+
39+
There are many ways to address this bug, please address it using a solution of your choice. Remember that a good solution is not only technically sound but should also result in good user experience.
40+
41+
## 2. Leaving comments
42+
43+
Make it possible for users to leave comments under published posts.
44+
45+
## 3. Typing indicator
46+
47+
You're probaly familiar with typing indicators such as [this one](https://support.signal.org/hc/en-us/articles/360020798451-Typing-Indicators).
48+
49+
Now that users can add comments, add a typing indicator to show others that someone is writing a comment.
50+
51+
## 4. Be prepared to discuss your solutions with us :)
52+
53+
During the technical interview, among other things, we're going to discuss how you approached the tasks. We're probably going to ask you about why you decided on a specific solution, pros and cons of different ways of addressing the same problem etc.
54+
55+
<br />
56+
57+
# ⬇️ PROJECT DOCUMENTATION ⬇️
58+
59+
# Fullstack Example with Next.js (REST API)
60+
61+
This example shows how to implement a **fullstack app in TypeScript with [Next.js](https://nextjs.org/)** using [React](https://reactjs.org/) (frontend), [Express](https://expressjs.com/) and [Prisma Client](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client) (backend). It uses a SQLite database file with some initial dummy data which you can find at [`./backend/prisma/dev.db`](./backend/prisma/dev.db).
62+
63+
## Getting Started
64+
65+
### 1. Install npm dependencies:
66+
67+
Install dependencies for your [`backend`](./backend). Open a terminal window and install the `backend`'s dependencies
68+
69+
```bash
70+
cd backend
71+
npm install
72+
```
73+
74+
Open a separate terminal window and navigate to your [`frontend`](./frontend) directory and install its dependencies
75+
76+
```bash
77+
cd frontend
78+
npm install
79+
```
80+
81+
### 2. Create and seed the database (backend)
82+
83+
On the terminal window used to install the backend npm dependencies, 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`](./backend/prisma/schema.prisma):
84+
85+
```
86+
npx prisma migrate dev --name init
87+
```
88+
89+
When `npx prisma migrate dev` is executed against a newly created database, seeding is also triggered. The seed file in [`prisma/seed.ts`](./backend/prisma/seed.ts) will be executed and your database will be populated with the sample data.
90+
91+
### 3. Start the server (backend)
92+
93+
On the same terminal used in step 2, run the following command to start the server:
94+
95+
```bash
96+
npm run dev
97+
```
98+
99+
The server is now running at [`http://localhost:3001/`](http://localhost:3001/).
100+
101+
### 4. Start the app (frontend)
102+
103+
On the terminal window used to install frontend npm dependencies, run the following command to start the app:
104+
105+
```bash
106+
npm run dev
107+
```
108+
109+
The app is now running, navigate to [`http://localhost:3000/`](http://localhost:3000/) in your browser to explore its UI.
110+
111+
<details><summary>Expand for a tour through the UI of the app</summary>
112+
113+
<br />
114+
115+
**Blog** (located in [`./pages/index.tsx`](./pages/index.tsx))
116+
117+
![](https://imgur.com/eepbOUO.png)
118+
119+
**Signup** (located in [`./pages/signup.tsx`](./pages/signup.tsx))
120+
121+
![](https://imgur.com/iE6OaBI.png)
122+
123+
**Create post (draft)** (located in [`./pages/create.tsx`](./pages/create.tsx))
124+
125+
![](https://imgur.com/olCWRNv.png)
126+
127+
**Drafts** (located in [`./pages/drafts.tsx`](./pages/drafts.tsx))
128+
129+
![](https://imgur.com/PSMzhcd.png)
130+
131+
**View post** (located in [`./pages/p/[id].tsx`](./pages/p/[id].tsx)) (delete or publish here)
132+
133+
![](https://imgur.com/zS1B11O.png)
134+
135+
</details>
136+
137+
## Using the REST API
138+
139+
You can also access the REST API of the API server directly. It is running [`localhost:3001`](http://localhost:3001) (so you can e.g. reach the API with [`localhost:3000/feed`](http://localhost:3001/feed)).
140+
141+
### `GET`
142+
143+
- `/api/post/:id`: Fetch a single post by its `id`
144+
- `/api/feed`: Fetch all _published_ posts
145+
- `/api/filterPosts?searchString={searchString}`: Filter posts by `title` or `content`
146+
147+
### `POST`
148+
149+
- `/api/post`: Create a new post
150+
- Body:
151+
- `title: String` (required): The title of the post
152+
- `content: String` (optional): The content of the post
153+
- `authorEmail: String` (required): The email of the user that creates the post
154+
- `/api/user`: Create a new user
155+
- Body:
156+
- `email: String` (required): The email address of the user
157+
- `name: String` (optional): The name of the user
158+
159+
### `PUT`
160+
161+
- `/api/publish/:id`: Publish a post by its `id`
162+
163+
### `DELETE`
164+
165+
- `/api/post/:id`: Delete a post by its `id`
166+
167+
## Evolving the app
168+
169+
Evolving the application typically requires three steps:
170+
171+
1. Migrate your database using Prisma Migrate
172+
1. Update your server-side application code
173+
1. Build new UI features in React
174+
175+
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.
176+
177+
### 1. Migrate your database using Prisma Migrate
178+
179+
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:
180+
181+
```diff
182+
// schema.prisma
183+
184+
model Post {
185+
id Int @default(autoincrement()) @id
186+
title String
187+
content String?
188+
published Boolean @default(false)
189+
author User? @relation(fields: [authorId], references: [id])
190+
authorId Int
191+
}
192+
193+
model User {
194+
id Int @default(autoincrement()) @id
195+
name String?
196+
email String @unique
197+
posts Post[]
198+
+ profile Profile?
199+
}
200+
201+
+model Profile {
202+
+ id Int @default(autoincrement()) @id
203+
+ bio String?
204+
+ userId Int @unique
205+
+ user User @relation(fields: [userId], references: [id])
206+
+}
207+
```
208+
209+
Once you've updated your data model, you can execute the changes against your database with the following command:
210+
211+
```
212+
npx prisma migrate dev
213+
```
214+
215+
### 2. Update your application code
216+
217+
You can now use your `PrismaClient` instance to perform operations against the new `Profile` table. Here are some examples:
218+
219+
#### Create a new profile for an existing user
220+
221+
```ts
222+
const profile = await prisma.profile.create({
223+
data: {
224+
bio: "Hello World",
225+
user: {
226+
connect: { email: "[email protected]" },
227+
},
228+
},
229+
});
230+
```
231+
232+
#### Create a new user with a new profile
233+
234+
```ts
235+
const user = await prisma.user.create({
236+
data: {
237+
238+
name: "John",
239+
profile: {
240+
create: {
241+
bio: "Hello World",
242+
},
243+
},
244+
},
245+
});
246+
```
247+
248+
#### Update the profile of an existing user
249+
250+
```ts
251+
const userWithUpdatedProfile = await prisma.user.update({
252+
where: { email: "[email protected]" },
253+
data: {
254+
profile: {
255+
update: {
256+
bio: "Hello Friends",
257+
},
258+
},
259+
},
260+
});
261+
```
262+
263+
### 3. Build new UI features in React
264+
265+
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 React. It could e.g. be called `profile.tsx` and would be located in the `pages` directory.
266+
267+
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.

backend/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
dist/
3+
*.env*

backend/package.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "rest-express",
3+
"version": "1.0.0",
4+
"license": "MIT",
5+
"scripts": {
6+
"dev": "ts-node src/index.ts"
7+
},
8+
"dependencies": {
9+
"@prisma/client": "3.14.0",
10+
"@types/node": "16.11.36",
11+
"cors": "2.8.5",
12+
"express": "4.18.1"
13+
},
14+
"devDependencies": {
15+
"@types/cors": "2.8.12",
16+
"@types/express": "4.17.13",
17+
"@types/node": "16.11.36",
18+
"prisma": "3.14.0",
19+
"ts-node": "10.7.0",
20+
"typescript": "4.6.4"
21+
},
22+
"prisma": {
23+
"seed": "ts-node prisma/seed.ts"
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- CreateTable
2+
CREATE TABLE "User" (
3+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4+
"email" TEXT NOT NULL,
5+
"name" TEXT
6+
);
7+
8+
-- CreateTable
9+
CREATE TABLE "Post" (
10+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
11+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
12+
"updatedAt" DATETIME NOT NULL,
13+
"title" TEXT NOT NULL,
14+
"content" TEXT,
15+
"published" BOOLEAN NOT NULL DEFAULT false,
16+
"viewCount" INTEGER NOT NULL DEFAULT 0,
17+
"authorId" INTEGER,
18+
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
19+
);
20+
21+
-- CreateIndex
22+
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Please do not edit this file manually
2+
# It should be added in your version-control system (i.e. Git)
3+
provider = "sqlite"

backend/prisma/schema.prisma

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
generator client {
2+
provider = "prisma-client-js"
3+
}
4+
5+
datasource db {
6+
provider = "sqlite"
7+
url = "file:./dev.db"
8+
}
9+
10+
model User {
11+
id Int @id @default(autoincrement())
12+
email String @unique
13+
name String?
14+
posts Post[]
15+
}
16+
17+
model Post {
18+
id Int @id @default(autoincrement())
19+
createdAt DateTime @default(now())
20+
updatedAt DateTime @updatedAt
21+
title String
22+
content String?
23+
published Boolean @default(false)
24+
viewCount Int @default(0)
25+
author User? @relation(fields: [authorId], references: [id])
26+
authorId Int?
27+
}

0 commit comments

Comments
 (0)