Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite297d43

Browse files
committed
init action package
1 parent6aea435 commite297d43

File tree

15 files changed

+1290
-103
lines changed

15 files changed

+1290
-103
lines changed

‎packages/action/.eslintrc.js‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports={
2+
root:true,
3+
extends:['@davstack/eslint-config/library.js'],
4+
parser:'@typescript-eslint/parser',
5+
parserOptions:{
6+
project:true,
7+
},
8+
};

‎packages/action/README.md‎

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#Davstack Service
2+
3+
Davstack Service is simple and flexible library for building backend services with TypeScript.
4+
5+
###Why Use Davstack Service?
6+
7+
- 🏠 Simple and familiar syntax - middleware, input and outputs inspired by trpc procedures
8+
- 🧩 Flexible - Works well with next js server actions as well as trpc
9+
- ✅ Typescript first - inferred input/output types and middleware
10+
11+
###Installation
12+
13+
```bash
14+
npm install zod @davstack/service
15+
```
16+
17+
Visit the[DavStack Service Docs](https://davstack.com/service/overview) for more information and examples, such as this[trpc usage example](https://davstack.com/service/trpc-usage-example).
18+
19+
##Demo Usage
20+
21+
- The service definition replaces tRPC procedures, but the syntax is very similar.
22+
- Once the service is integrated into tRPC routers, the API is the same as any other tRPC router.
23+
24+
##Composing Services example
25+
26+
```ts
27+
// api/services/invoice.ts
28+
import {authedService,publicService }from'@/lib/service';
29+
30+
// Service composed from range of other services:
31+
32+
exportconst mailAiGeneratedInvoice=authedService
33+
.input(z.object({ to:z.string(), projectId:z.string() }))
34+
.query(async ({ctx,input })=> {
35+
awaitcheckSufficientCredits(ctx, { amount:10 });
36+
37+
const pdf=awaitgeneratePdf(ctx, { html:project.invoiceHtml });
38+
39+
awaitsendEmail(ctx, {
40+
to:input.to,
41+
subject:'Invoice',
42+
body:'Please find attached your invoice',
43+
attachments: [{ filename:'invoice.pdf', content:pdf }],
44+
});
45+
46+
awaitdeductCredits(ctx, { amount:10 });
47+
48+
return'Invoice sent';
49+
});
50+
51+
exportconst generatePdf=authedService
52+
.input(z.object({ html:z.string() }))
53+
.query(async ({ctx,input })=> {
54+
// complex business logic here
55+
returnpdf;
56+
});
57+
58+
exportconst sendEmail=authedService
59+
.input(z.object({ to:z.string(), subject:z.string(), body:z.string() }))
60+
.query(async ({ctx,input })=> {
61+
// complex business logic here
62+
return'Email sent';
63+
});
64+
65+
exportconst checkSufficientCredits=authedService
66+
.input(z.object({ amount:z.number() }))
67+
.query(async ({ctx,input })=> {
68+
// complex business logic here
69+
return'Sufficient funds';
70+
});
71+
72+
// ... etc
73+
```
74+
75+
Integrate your services with tRPC with 0 boilerplate. Works just like any other tRPC router.
76+
77+
```ts
78+
// api/router.ts
79+
80+
import*asinvoiceServicesfrom'@/api/services/invoice';
81+
import {createTRPCRouter }from'@/lib/trpc';
82+
import {
83+
createTrpcProcedureFromService,
84+
createTrpcRouterFromServices,
85+
}from'@davstack/service';
86+
87+
exportconst appRouter=createTRPCRouter({
88+
invoice:createTrpcRouterFromServices(invoiceServices),
89+
});
90+
```
91+
92+
###Middleware Example
93+
94+
Define your services with reusable middleware in a separate file, and export them for reuse.
95+
96+
```ts
97+
// lib/service.ts
98+
import {service }from'@davstack/service';
99+
import {db }from'@/lib/db';
100+
101+
// Define the context types for your services
102+
exporttypePublicServiceCtx= {
103+
user: { id:string; role:string }|undefined;
104+
db:typeofdb;
105+
};
106+
exporttypeAuthedServiceCtx=Required<PublicServiceCtx>;
107+
108+
// export your services
109+
exportconst publicService=service<PublicServiceCtx>();
110+
111+
exportconst authedService=service<AuthedServiceCtx>().use(
112+
async ({ctx,next })=> {
113+
// Only allows authenticate users to access this service
114+
if (!ctx.user) {
115+
thrownewError('Unauthorized');
116+
}
117+
returnnext(ctx);
118+
}
119+
);
120+
121+
exportfunction createServiceCtx() {
122+
const user=auth();
123+
return {user,db };
124+
}
125+
```
126+
127+
Import the public / authed service builders from the service
128+
129+
```ts
130+
// api/services/some-service.ts
131+
import {publicService,authedService }from'@/lib/service';
132+
133+
exportconst getSomePublicData=publicService.query(async ({ctx })=> {
134+
return'Public data';
135+
});
136+
137+
exportconst getSomeUserData=authedService.query(async ({ctx })=> {
138+
// will throw an error if ctx.user is undefined
139+
return'Protected data';
140+
});
141+
```
142+
143+
Specify the input and output schemas for your service for validation and type safety, and use the ctx/input arguments to access the service context and input data.
144+
145+
```ts
146+
// api/services/task-services.ts
147+
import {service }from'@davstack/service';
148+
import {z }from'zod';
149+
150+
const getTasks=service()
151+
.input(z.object({ projectId:z.string() }))
152+
.query(async ({ctx,input })=> {
153+
returnctx.db.tasks.findMany({ where: { projectId:input.projectId } });
154+
});
155+
```
156+
157+
###Direct Service Usage
158+
159+
Unlike tRPC procedures, services can be called directly from anywhere in your backend, including within other services.
160+
161+
```typescript
162+
const ctx=createServiceCtx();// or get ctx from parent service
163+
const tasks=awaitgetTasks(ctx, { projectId:'...' });
164+
```
165+
166+
This allows you to build complex service logic by composing multiple services together.
167+
168+
```typescript
169+
const getProjectDetails=service()
170+
.input(z.object({ projectId:z.string() }))
171+
.output(
172+
z.object({
173+
id:z.string(),
174+
name:z.string(),
175+
tasks:getTasks.outputSchema,
176+
})
177+
)
178+
.query(async ({ctx,input })=> {
179+
const project=awaitgetProject(ctx, { projectId:input.projectId });
180+
const tasks=awaitgetTasks(ctx, { projectId:input.projectId });
181+
return {...project,tasks };
182+
});
183+
```
184+
185+
###tRPC Integration
186+
187+
Seamlessly integrate with tRPC to create type-safe API endpoints.
188+
189+
```ts
190+
import {initTRPC }from'@trpc/server';
191+
import {createTrpcRouterFromServices }from'@davstack/service';
192+
import*astaskServicesfrom'./services/tasks';
193+
import*asprojectServicesfrom'./services/projects';
194+
import {sendFeedback }from'./services/send-feedback';
195+
196+
const t=initTRPC();
197+
198+
const appRouter=t.router({
199+
tasks:createTrpcRouterFromServices(taskServices),
200+
projects:createTrpcRouterFromServices(projectServices),
201+
// or create a single procedure from a service
202+
sendFeedback:createTrpcProcedureFromService(sendFeedback),
203+
});
204+
```
205+
206+
NOTE: it is recommended to use the`* as yourServicesName` syntax. Otherwise, ctrl+click on the tRPC client handler will navigate you to the app router file, instead of the specific service definition.
207+
208+
###Acknowledgements
209+
210+
Davstack Store has been heavily inspired by[tRPC](https://trpc.io/), a fantastic library for building type-safe APIs. A big shout-out to the tRPC team for their amazing work.
211+
212+
Nick-Lucas, a tRPC contributor, inspired the creation of Davstack Service with his[github comment](https://github.com/trpc/trpc/discussions/4839#discussioncomment-8224476). He suggested "making controllers minimal" and "to separate your business logic from the API logic", which is exactly what Davstack Service aims to do.
213+
214+
###Contributing
215+
216+
Contributions are welcome! Please read our[contributing guide](link-to-contributing-guide) for details on our code of conduct and the submission process.
217+
218+
###License
219+
220+
This project is licensed under the[MIT License](link-to-license). See the LICENSE file for details.

‎packages/action/package.json‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name":"@davstack/action",
3+
"version":"0.0.1",
4+
"main":"./dist/index.js",
5+
"module":"./dist/index.mjs",
6+
"types":"./dist/index.d.ts",
7+
"sideEffects":false,
8+
"license":"MIT",
9+
"files": [
10+
"dist/**"
11+
],
12+
"scripts": {
13+
"build":"tsup src/index.ts --format esm,cjs --dts --dts-resolve",
14+
"dev":"tsup src/index.ts --format esm,cjs --watch --dts --dts-resolve",
15+
"lint":"eslint\"src/**/*.ts*\"",
16+
"clean":"rm -rf .turbo && rm -rf node_modules && rm -rf dist"
17+
},
18+
"devDependencies": {
19+
"@davstack/eslint-config":"workspace:*",
20+
"@davstack/tsconfig":"workspace:*",
21+
"@types/react":"^18.2.61",
22+
"@types/react-dom":"^18.2.19",
23+
"eslint":"^8.57.0",
24+
"react":"^18.2.0",
25+
"tsup":"^8.0.2",
26+
"typescript":"^5.3.3"
27+
},
28+
"publishConfig": {
29+
"access":"public"
30+
},
31+
"dependencies": {
32+
"zod":"^3.22.4"
33+
}
34+
}

‎packages/action/src/index.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export*from'./service';
2+
export*from'./trpc-helpers';

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp