Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 10.14.0
version: 10.15.0

- name: Install Node.js
uses: actions/setup-node@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/bump_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 10.14.0
version: 10.15.0

- name: Install Node.js
uses: actions/setup-node@v4
Expand Down
21 changes: 11 additions & 10 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,30 @@
"drizzle-kit": "drizzle-kit"
},
"dependencies": {
"@hono/zod-openapi": "^1.0.2",
"@hono/zod-openapi": "^1.1.0",
"@hono/zod-validator": "^0.7.2",
"@vitnode/core": "workspace:*",
"drizzle-kit": "^0.31.4",
"drizzle-orm": "^0.44.4",
"hono": "^4.8.10",
"hono": "^4.9.2",
"next-intl": "^4.3.4",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"zod": "^4.0.14"
"use-intl": "^4.3.4",
"zod": "^4.0.17"
},
"devDependencies": {
"@hono/node-server": "^1.18.0",
"@react-email/components": "^0.3.3",
"@types/node": "^24.1.0",
"@types/react": "^19.1.9",
"@hono/node-server": "^1.19.0",
"@react-email/components": "^0.5.1",
"@types/node": "^24.3.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitnode/eslint-config": "workspace:*",
"dotenv": "^17.2.1",
"eslint": "^9.32.0",
"react-email": "^4.2.7",
"eslint": "^9.33.0",
"react-email": "^4.2.8",
"tsc-alias": "^1.8.16",
"tsx": "^4.20.3",
"tsx": "^4.20.4",
"typescript": "^5.9.2"
}
}
33 changes: 32 additions & 1 deletion apps/api/src/locales/@vitnode/core/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@
},
"password": {
"label": "Password",
"required": "Password is required."
"required": "Password is required.",
"reset": "Forgot password?"
},
"errors": {
"access_denied": {
Expand All @@ -141,6 +142,36 @@
}
},
"submit": "Login"
},
"reset_password": {
"title": "Reset Password",
"desc": "Please enter your email address below to receive a password reset link.",
"submit": "Send Reset Link",
"confirmation": {
"title": "Check your email",
"desc": "We've sent a password reset link to your email address:",
"check_spam": "If you don't see the email in your inbox, please check your spam folder."
},
"email": {
"subject": "Reset your password",
"greeting": "Hello {name}!",
"intro": "We received a request to reset your password for your account.",
"button": "Reset Password",
"instructions": "Click the button above to reset your password. This link will expire in 30 minutes for security reasons.",
"no_action": "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.",
"security_note": "For your security, this request was made from IP address: {ip}",
"help": "If you're having trouble with the button above, copy and paste the URL below into your web browser:",
"expire_time": "This link will expire on {date}"
}
},
"change_password": {
"title": "Change Password",
"desc": "Please enter your new password below.",
"submit": "Change Password",
"success": {
"title": "Password changed successfully",
"desc": "You can now log in with your new password."
}
}
}
},
Expand Down
5 changes: 5 additions & 0 deletions apps/docs/content/docs/dev/api/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "REST API",
"defaultOpen": true,
"pages": ["modules", "..."]
}
58 changes: 58 additions & 0 deletions apps/docs/content/docs/dev/api/modules.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: Modules
description: xxx
---

## Usage

Think of modules as containers for related API endpoints. They help organize your routes logically - perfect for keeping your sanity intact!

```ts title="plugins/blog/src/api/modules/categories/categories.module.ts"
import { buildModule } from '@vitnode/core/api/lib/module';

import { CONFIG_PLUGIN } from '@/config';

export const categoriesModule = buildModule({
pluginId: CONFIG_PLUGIN.id,
name: 'categories',
routes: [], // We'll populate this soon!
});
```

#### Nested Modules

Want to create a module hierarchy? VitNode's got your back! Nested modules are perfect for complex APIs.

```ts title="plugins/blog/src/api/modules/categories/categories.module.ts"
import { buildModule } from '@vitnode/core/api/lib/module';

import { CONFIG_PLUGIN } from '@/config';

import { postsModule } from './posts/posts.module'; // [!code ++]

export const categoriesModule = buildModule({
pluginId: CONFIG_PLUGIN.id,
name: 'categories',
routes: [],
modules: [postsModule], // [!code ++]
});
```

This creates a structure: `/api/{plugin_id}/categories/posts/*`

## Connecting Modules to the API

```ts title="plugins/blog/src/config.api.ts"
import { buildApiPlugin } from '@vitnode/core/api/lib/plugin';

import { CONFIG_PLUGIN } from '@/config';

import { categoriesModule } from './api/modules/categories/categories.module'; // [!code ++]

export const blogApiPlugin = () => {
return buildApiPlugin({
...CONFIG_PLUGIN,
modules: [categoriesModule], // [!code ++]
});
};
```
Original file line number Diff line number Diff line change
@@ -1,56 +1,9 @@
---
title: Restful API
description: Learn how to create and organize API routes in your VitNode plugins with modules, handlers, and parameter validation.
title: Routes
description: xxx
---

Building a RESTful API in VitNode is like assembling LEGO blocks - you create modules, add routes to them, and connect everything together. Let's dive into this architectural adventure!

## Setting Up Your API Structure

<Steps>
<Step>

### Create Module

Think of modules as containers for related API endpoints. They help organize your routes logically - perfect for keeping your sanity intact!

```ts title="plugins/blog/src/api/modules/categories/categories.module.ts"
import { buildModule } from '@vitnode/core/api/lib/module';

import { CONFIG_PLUGIN } from '@/config';

export const categoriesModule = buildModule({
pluginId: CONFIG_PLUGIN.id,
name: 'categories',
routes: [], // We'll populate this soon!
});
```

#### Nested Modules

Want to create a module hierarchy? VitNode's got your back! Nested modules are perfect for complex APIs.

```ts title="plugins/blog/src/api/modules/categories/categories.module.ts"
import { buildModule } from '@vitnode/core/api/lib/module';

import { CONFIG_PLUGIN } from '@/config';

import { postsModule } from './posts/posts.module'; // [!code ++]

export const categoriesModule = buildModule({
pluginId: CONFIG_PLUGIN.id,
name: 'categories',
routes: [],
modules: [postsModule], // [!code ++]
});
```

This creates a beautiful structure: `/api/{plugin_id}/categories/posts/*`

</Step>
<Step>

### Craft Routes
## Usage

Now for the fun part - creating actual endpoints! Each route is a small but mighty function that handles HTTP requests.

Expand Down Expand Up @@ -96,29 +49,7 @@ export const getCategoriesRoute = buildRoute({
});
```

</Step>
<Step>

### Connect Everything

The final step is connecting your modules to your plugin's API configuration. It's like plugging in the last cable!

```ts title="plugins/blog/src/config.api.ts"
import { buildApiPlugin } from '@vitnode/core/api/lib/plugin';

import { CONFIG_PLUGIN } from '@/config';

import { categoriesModule } from './api/modules/categories/categories.module'; // [!code ++]

export const blogApiPlugin = () => {
return buildApiPlugin({
...CONFIG_PLUGIN,
modules: [categoriesModule], // [!code ++]
});
};
```

Don't forget to add your routes to the module:
### Connecting Routes to Modules

```ts title="plugins/blog/src/api/modules/categories/categories.module.ts"
import { buildModule } from '@vitnode/core/api/lib/module';
Expand All @@ -133,10 +64,7 @@ export const categoriesModule = buildModule({
});
```

</Step>
</Steps>

## Working with Parameters
## Inputs

### Path Parameters

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/dev/captcha/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export const mutationApi = async ({
captchaToken, // [!code ++]
...input
// [!code ++]
}: z.infer<typeof zodSignUpSchema> & { captchaToken }) => {
}: z.infer<typeof zodSignUpSchema> & { captchaToken: string }) => {
const res = await fetcher(usersModule, {
path: '/sign_up',
method: 'post',
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/dev/email/components/button.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import buttonPreviewImg from './button-preview.png';
## Usage

```ts
import { Button } from '@vitnode/core/emails/ui/button';
import { EmailButton } from '@vitnode/core/emails/ui/button';
```

```tsx
<Button href="https://vitnode.com/">Default</Button>
<EmailButton href="https://vitnode.com/">Default</EmailButton>
```

## Props
Expand Down
30 changes: 15 additions & 15 deletions apps/docs/content/docs/dev/email/components/card.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,29 @@ import cardPreviewImg from './card-preview.png';

```tsx
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
EmailCard,
EmailCardContent,
EmailCardDescription,
EmailCardFooter,
EmailCardHeader,
EmailCardTitle,
} from '@vitnode/core/emails/ui/card';
import { Text } from '@react-email/components';
```

```tsx
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card Description</CardDescription>
<EmailCard>
<EmailCardHeader>
<EmailCardTitle>Card Title</EmailCardTitle>
<EmailCardDescription>Card Description</EmailCardDescription>
</CardHeader>
<CardContent>
<EmailCardContent>
<Text className="m-0">
This is the content of the card. You can put any content here.
</Text>
</CardContent>
<CardFooter>
</EmailCardContent>
<EmailCardFooter>
<Text className="m-0">Footer</Text>
</CardFooter>
</Card>
</EmailCardFooter>
</EmailCard>
```
Loading