Blogging
Blogging
3. zod as the validation library, type inference for the frontend types
https://projects.100xdevs.com/pdf/blog/blog-1 1/29
7/8/24, 11:35 PM DailyCode
💡 Reference https://hono.dev/top
https://projects.100xdevs.com/pdf/blog/blog-1 2/29
7/8/24, 11:35 PM DailyCode
1. POST /api/v1/user/signup
2. POST /api/v1/user/signin
3. POST /api/v1/blog
4. PUT /api/v1/blog
5. GET /api/v1/blog/:id
6. GET /api/v1/blog/bulk
💡 https://hono.dev/api/routing
Solution
https://projects.100xdevs.com/pdf/blog/blog-1 3/29
7/8/24, 11:35 PM DailyCode
postgres://avnadmin:password@host/db Copy
https://projects.100xdevs.com/pdf/blog/blog-1 4/29
7/8/24, 11:35 PM DailyCode
prisma://accelerate.prisma-data.net/?api_key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ Co
DATABASE_URL="postgres://avnadmin:password@host/db" Copy
name = "backend" Co
compatibility_date = "2023-12-01"
[vars]
DATABASE_URL = "prisma://accelerate.prisma-data.net/?api_key=eyJhbGciOiJIUzI1NiIsInR5
💡 You should not have your prod URL committed either in .env or in wrangler.toml to
github
wranger.toml should have a dev/local DB url
.env should be in .gitignore
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
name String?
password String
posts Post[]
}
https://projects.100xdevs.com/pdf/blog/blog-1 5/29
7/8/24, 11:35 PM DailyCode
model Post {
id String @id @default(uuid())
title String
content String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
}
💡 You might face issues here, try changing your wifi if that happens
https://projects.100xdevs.com/pdf/blog/blog-1 6/29
7/8/24, 11:35 PM DailyCode
💡 To get the right types on c.env , when initializing the Hono app, pass the types of env
as a generic
💡 Ideally you shouldn’t store passwords in plaintext. You should hash before storing them.
More details on how you can do that -
https://community.cloudflare.com/t/options-for-password-hashing/138077
https://developers.cloudflare.com/workers/runtime-apis/web-crypto/
https://projects.100xdevs.com/pdf/blog/blog-1 7/29
7/8/24, 11:35 PM DailyCode
Solution
Copy
app.post('/api/v1/signin', async (c) => {
const prisma = new PrismaClient({
datasourceUrl: c.env?.DATABASE_URL ,
}).$extends(withAccelerate());
https://projects.100xdevs.com/pdf/blog/blog-1 8/29
7/8/24, 11:35 PM DailyCode
if (!user) {
c.status(403);
return c.json({ error: "user not found" });
}
Step 6 - Middlewares
Creating a middleware in hono is well documented - https://hono.dev/guides/middleware
Copy
app.get('/api/v1/blog/:id', (c) => {})
https://projects.100xdevs.com/pdf/blog/blog-1 9/29
7/8/24, 11:35 PM DailyCode
How to make sure the types of variables that are being passed is correct?
Solution
https://projects.100xdevs.com/pdf/blog/blog-1 10/29
7/8/24, 11:35 PM DailyCode
Send the Header from Postman and ensure that the user id gets logged on the server
Callout
💡 If you want, you can extract the prisma variable in a global middleware that set’s it on
the context variable
Ref https://stackoverflow.com/questions/75554786/use-cloudflare-worker-env-outside-fetch-
scope
https://projects.100xdevs.com/pdf/blog/blog-1 11/29
7/8/24, 11:35 PM DailyCode
Better routing
https://hono.dev/api/routing#grouping
Hono let’s you group routes together so you can have a cleaner file structure.
routes/user.ts
routes/blog.ts
and push the user routes to user.ts
index.ts
app.route('/api/v1/user', userRouter)
app.route('/api/v1/book', bookRouter)
user.ts
https://projects.100xdevs.com/pdf/blog/blog-1 12/29
7/8/24, 11:35 PM DailyCode
return c.json({
jwt: token
})
})
if (!user) {
c.status(403);
return c.json({ error: "user not found" });
}
Blog routes
https://projects.100xdevs.com/pdf/blog/blog-1 13/29
7/8/24, 11:35 PM DailyCode
https://projects.100xdevs.com/pdf/blog/blog-1 14/29
7/8/24, 11:35 PM DailyCode
return c.json(post);
})
return c.json(posts);
})
Try to hit the routes via POSTMAN and ensure they work as expected
https://projects.100xdevs.com/pdf/blog/blog-1 15/29
7/8/24, 11:35 PM DailyCode
Bindings
https://hono.dev/getting-started/cloudflare-workers#bindings
https://projects.100xdevs.com/pdf/blog/blog-1 16/29
7/8/24, 11:35 PM DailyCode
Variables
https://hono.dev/api/context#var
If you wan’t to get and set values on the context of the request, you can use c.get and c.set
You need to make typescript aware of the variables that you will be setting on the context.
https://projects.100xdevs.com/pdf/blog/blog-1 17/29
7/8/24, 11:35 PM DailyCode
💡 You can also create a middleware that sets prisma in the context so you don’t need to
initialise it in the function body again and again
💡 Make sure you have logged in the cloudflare cli using npx wrangler login
https://projects.100xdevs.com/pdf/blog/blog-1 18/29
7/8/24, 11:35 PM DailyCode
https://projects.100xdevs.com/pdf/blog/blog-1 19/29
7/8/24, 11:35 PM DailyCode
If you’ve gone through the video Cohort 1 - Deploying npm packages, Intro to Monorepos ,
you’ll notice we introduced type inference in Zod
https://zod.dev/?id=type-inference
1. Update tsconfig.json
3. Update the name in package.json to be in your own npm namespace, Update main to be
dist/index.js
{ Copy
"name": "@100xdevs/common-app",
"version": "1.0.0",
We will"description":
divide our project
"",into 3 parts
"main": "dist/index.js",
1. Backend
"scripts": {
2. Frontend
"test": "echo \"Error: no test specified\" && exit 1"
},
3. common
"keywords": [],
will contain
"author":
common "",all the things that frontend and backend want to share.
We will"license":
make common an independent npm module for now.
"ISC"
Eventually, we will see how monorepos make it easier to have multiple packages sharing code in
}
the same repo
1. Add src to .npmignore
2. Install zod
1. signupInput / SignupInput
2. signinInput / SigninInput
https://projects.100xdevs.com/pdf/blog/blog-1 20/29
7/8/24, 11:35 PM DailyCode
3. createPostInput / CreatePostInput
4. updatePostInput / UpdatePostInput
Solution
2. Publish to npm
https://projects.100xdevs.com/pdf/blog/blog-1 21/29
7/8/24, 11:35 PM DailyCode
cd backend Copy
cd node_modules/your_package_name Copy
Solution
https://projects.100xdevs.com/pdf/blog/blog-1 22/29
7/8/24, 11:35 PM DailyCode
c.set('userId', payload.id);
await next()
})
if (!user) {
c.status(403);
return c.json({ error: "user not found" });
}
https://projects.100xdevs.com/pdf/blog/blog-1 23/29
7/8/24, 11:35 PM DailyCode
return c.json(post);
})
https://projects.100xdevs.com/pdf/blog/blog-1 24/29
7/8/24, 11:35 PM DailyCode
if (!success) {
c.status(400);
return c.json({ error: "invalid input" });
}
prisma.post.update({
where: {
id: body.id,
authorId: userId
},
data: {
title: body.title,
content: body.content
}
});
1. Initialise tailwind
https://tailwindcss.com/docs/guides/vite
1. Update tailwind.config.js
https://projects.100xdevs.com/pdf/blog/blog-1 25/29
7/8/24, 11:35 PM DailyCode
],
theme: {
extend: {},
},
plugins: [],
}
1. Update index.css
1. Empty up App.css
1. Add routing (ensure you create the Signup, Signin and Blog components)
function App() {
https://projects.100xdevs.com/pdf/blog/blog-1 26/29
7/8/24, 11:35 PM DailyCode
return (
<>
<BrowserRouter>
<Routes>
<Route path="/signup" element={<Signup />} />
<Route path="/signin" element={<Signin />} />
<Route path="/blog/:id" element={<Blog />} />
</Routes>
</BrowserRouter>
</>
)
}
https://projects.100xdevs.com/pdf/blog/blog-1 27/29
7/8/24, 11:35 PM DailyCode
Designs generated from v0.dev - an AI service by vercel that lets you generate frontends
Signup page
Blogs page
https://projects.100xdevs.com/pdf/blog/blog-1 28/29
7/8/24, 11:35 PM DailyCode
Blogs page
https://projects.100xdevs.com/pdf/blog/blog-1 29/29