@@ -16,7 +16,8 @@ will display a list of dinosaurs. When you click on one, it'll take you to a
16
16
dinosaur page with more details. You can see the
17
17
[ finished app on GitHub] ( https://github.com/denoland/tutorial-with-vue ) .
18
18
19
- ![ The Vue.js app in action] ( ./images/how-to/vue/vue.gif )
19
+ You can see a live version of the app on
20
+ [ Deno Deploy] ( https://tutorial-with-vue.deno.deno.net/ ) .
20
21
21
22
## Create a Vue.js app with Vite and Deno
22
23
@@ -63,101 +64,162 @@ level of your `deno.json` file:
63
64
"unstable" : [" fmt-component" ]
64
65
```
65
66
66
- ## Add a backend
67
+ ## Add a backend API
67
68
68
- The next step is to add a backend API. We'll create a very simple API that
69
- returns information about dinosaurs .
69
+ We'll build an API server using Deno and Oak. This will be where we get our
70
+ dinosaur data .
70
71
71
- In the root of your new vite project, create an ` api ` folder. In that folder,
72
- create a ` main.ts ` file, which will run the server, and a ` data.json ` , which
73
- where we'll put the hard coded data.
72
+ In the root directory of your project, create an ` api ` folder. In that folder,
73
+ create a ` data.json ` , which will contain the hard coded dinosaur data.
74
74
75
75
Copy and paste
76
- [ this json file] ( https://raw.githubusercontent.com/denoland/tutorial-with-vue/refs/heads/main/api/data.json )
77
- into ` api/data.json ` .
76
+ [ this json file] ( https://github.com/denoland/tutorial-with-react/blob/main/api/data.json )
77
+ into the ` api/data.json ` file. (If you were building a real app, you would
78
+ probably fetch this data from a database or an external API.)
78
79
79
- We're going to build out a simple API server with routes that return dinosaur
80
- information. We'll use the [ ` oak ` middleware framework ] ( https://jsr.io/@oak/oak )
81
- and the [ ` cors ` middleware] ( https://jsr.io/@tajpouria/cors ) to enable
80
+ We're going to build out some API routes that return dinosaur information. We'll
81
+ need Oak for the HTTP server and
82
+ [ CORS middleware] ( https://jsr.io/@tajpouria/cors ) to enable
82
83
[ CORS] ( https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS ) .
83
84
84
- Use the ` deno add ` command to add the required dependencies to your project :
85
+ Add the dependencies to your ` deno.json ` file by updating the imports section :
85
86
86
- ``` shell
87
- deno add jsr:@oak/oak jsr:@tajpouria/cors
87
+ ``` json title="deno.json"
88
+ {
89
+ "imports" : {
90
+ "@oak/oak" : " jsr:@oak/oak@^17.1.5" ,
91
+ "@tajpouria/cors" : " jsr:@tajpouria/cors@^1.2.1" ,
92
+ "vue-router" : " npm:vue-router@^4.5.1"
93
+ }
94
+ }
88
95
```
89
96
90
- Next, update ` api/main.ts ` to import the required modules and create a new
97
+ Next, create ` api/main.ts ` and import the required modules and create a new
91
98
` Router ` instance to define some routes:
92
99
93
- ``` ts title="main.ts"
100
+ ``` ts title="api/ main.ts"
94
101
import { Application , Router } from " @oak/oak" ;
95
102
import { oakCors } from " @tajpouria/cors" ;
103
+ import routeStaticFilesFrom from " ./util/routeStaticFilesFrom.ts" ;
96
104
import data from " ./data.json" with { type: " json" };
97
105
106
+ export const app = new Application ();
98
107
const router = new Router ();
99
108
```
100
109
101
- After this, in the same file, we'll define three routes. The first route at ` / `
102
- will return the string ` Welcome to the dinosaur API ` , then we'll set up
103
- ` /dinosaurs ` to return all the dinosaurs, and finally ` /dinosaurs/:dinosaur ` to
104
- return a specific dinosaur based on the name in the URL:
105
-
106
- ``` ts title="main.ts"
107
- router
108
- .get (" /" , (context ) => {
109
- context .response .body = " Welcome to dinosaur API!" ;
110
- })
111
- .get (" /dinosaurs" , (context ) => {
112
- context .response .body = data ;
113
- })
114
- .get (" /dinosaurs/:dinosaur" , (context ) => {
115
- if (! context ?.params ?.dinosaur ) {
116
- context .response .body = " No dinosaur name provided." ;
117
- }
110
+ After this, in the same file, we'll define two routes. One at ` /api/dinosaurs `
111
+ to return all the dinosaurs, and ` /api/dinosaurs/:dinosaur ` to return a specific
112
+ dinosaur based on the name in the URL:
118
113
119
- const dinosaur = data .find ((item ) =>
120
- item .name .toLowerCase () === context .params .dinosaur .toLowerCase ()
121
- );
114
+ ``` ts title="api/main.ts"
115
+ router .get (" /api/dinosaurs" , (context ) => {
116
+ context .response .body = data ;
117
+ });
122
118
123
- context .response .body = dinosaur ? dinosaur : " No dinosaur found." ;
124
- });
119
+ router .get (" /api/dinosaurs/:dinosaur" , (context ) => {
120
+ if (! context ?.params ?.dinosaur ) {
121
+ context .response .body = " No dinosaur name provided." ;
122
+ }
123
+
124
+ const dinosaur = data .find ((item ) =>
125
+ item .name .toLowerCase () === context .params .dinosaur .toLowerCase ()
126
+ );
127
+
128
+ context .response .body = dinosaur ?? " No dinosaur found." ;
129
+ });
125
130
```
126
131
127
- Finally, at the bottom of the same file, create a new ` Application ` instance and
128
- attach the routes we just defined to the application using
129
- ` app.use(router.routes()) ` and start the server listening on port 8000:
132
+ At the bottom of the same file, attach the routes we just defined to the
133
+ application. We also must include the static file server, and finally we'll
134
+ start the server listening on port 8000:
130
135
131
- ``` ts title="main.ts"
132
- const app = new Application ();
136
+ ``` ts title="api/main.ts"
133
137
app .use (oakCors ());
134
138
app .use (router .routes ());
135
139
app .use (router .allowedMethods ());
140
+ app .use (routeStaticFilesFrom ([
141
+ ` ${Deno .cwd ()}/dist ` ,
142
+ ` ${Deno .cwd ()}/public ` ,
143
+ ]));
144
+
145
+ if (import .meta .main ) {
146
+ console .log (" Server listening on port http://localhost:8000" );
147
+ await app .listen ({ port: 8000 });
148
+ }
149
+ ```
136
150
137
- await app .listen ({ port: 8000 });
151
+ You'll also need to create the ` api/util/routeStaticFilesFrom.ts ` file to serve
152
+ static files:
153
+
154
+ ``` ts title="api/util/routeStaticFilesFrom.ts"
155
+ import { Context , Next } from " @oak/oak" ;
156
+
157
+ // Configure static site routes so that we can serve
158
+ // the Vite build output and the public folder
159
+ export default function routeStaticFilesFrom(staticPaths : string []) {
160
+ return async (context : Context <Record <string , object >>, next : Next ) => {
161
+ for (const path of staticPaths ) {
162
+ try {
163
+ await context .send ({ root: path , index: " index.html" });
164
+ return ;
165
+ } catch {
166
+ continue ;
167
+ }
168
+ }
169
+
170
+ await next ();
171
+ };
172
+ }
138
173
```
139
174
140
- You can run the API server with ` deno run --allow-env --allow-net api/main.ts ` .
141
- We'll create a task to run this command and update the dev task to run both the
142
- Vue.js app and the API server.
175
+ You can run the API server with
176
+ ` deno run --allow-env --allow-net --allow-read api/main.ts ` . We'll create a task
177
+ to run this command in the background and update the dev task to run both the
178
+ Vue app and the API server.
143
179
144
- In your ` package.json ` file, update the ` scripts ` field to include the
145
- following:
180
+ Update your ` package.json ` scripts to include the following:
146
181
147
- ``` jsonc
182
+ ``` json title="package.json"
148
183
{
149
184
"scripts" : {
150
185
"dev" : " deno task dev:api & deno task dev:vite" ,
151
186
"dev:api" : " deno run --allow-env --allow-net api/main.ts" ,
152
187
"dev:vite" : " deno run -A npm:vite" ,
153
- // ...
188
+ "build" : " deno run -A npm:vite build" ,
189
+ "server:start" : " deno run -A --watch ./api/main.ts" ,
190
+ "serve" : " deno run build && deno run server:start" ,
191
+ "preview" : " vite preview"
192
+ }
154
193
}
155
194
```
156
195
157
- Now, if you run ` deno task dev` and visit ` localhost: 8000 ` , in your browser you
158
- should see the text ` Welcome to dinosaur API ! ` , and if you visit
159
- ` localhost: 8000 / dinosaurs` , you should see a JSON response of all of the
160
- dinosaurs.
196
+ Make sure your ` vite.config.ts ` includes the Deno plugin and proxy configuration
197
+ for development:
198
+
199
+ ``` ts title="vite.config.ts"
200
+ import { defineConfig } from " vite" ;
201
+ import vue from " @vitejs/plugin-vue" ;
202
+ import deno from " @deno/vite-plugin" ;
203
+
204
+ export default defineConfig ({
205
+ server: {
206
+ port: 3000 ,
207
+ proxy: {
208
+ " /api" : {
209
+ target: " http://localhost:8000" ,
210
+ changeOrigin: true ,
211
+ },
212
+ },
213
+ },
214
+ plugins: [vue (), deno ()],
215
+ optimizeDeps: {
216
+ include: [" react/jsx-runtime" ],
217
+ },
218
+ });
219
+ ```
220
+
221
+ If you run ` npm run dev ` now and visit ` localhost:8000/api/dinosaurs ` , in your
222
+ browser you should see a JSON response of all of the dinosaurs.
161
223
162
224
## Build the frontend
163
225
@@ -182,10 +244,16 @@ createApp(App)
182
244
.mount (" #app" );
183
245
```
184
246
185
- Add the Vue Router module to the project with ` deno add ` :
247
+ Add the Vue Router module to the project by updating your ` deno.json ` imports :
186
248
187
- ` ` ` shell
188
- deno add npm: vue- router
249
+ ``` json title="deno.json"
250
+ {
251
+ "imports" : {
252
+ "@oak/oak" : " jsr:@oak/oak@^17.1.5" ,
253
+ "@tajpouria/cors" : " jsr:@tajpouria/cors@^1.2.1" ,
254
+ "vue-router" : " npm:vue-router@^4.5.1"
255
+ }
256
+ }
189
257
```
190
258
191
259
Next, create a ` router ` directory in the ` src ` directory. In it, create an
@@ -255,21 +323,21 @@ up earlier and render them as links using the
255
323
256
324
export default defineComponent ({
257
325
async setup () {
258
- const res = await fetch (" http://localhost:8000 /dinosaurs" );
326
+ const res = await fetch (" /api /dinosaurs" );
259
327
const dinosaurs = await res .json () as Dinosaur[];
260
328
return { dinosaurs };
261
329
},
262
330
});
263
331
</script >
264
332
265
333
<template >
266
- < div v- for = " dinosaur in dinosaurs" : key= " dinosaur.name" >
334
+ <span v-for =" dinosaur in dinosaurs" :key =" dinosaur.name" >
267
335
<RouterLink
268
336
:to =" { name: 'Dinosaur', params: { dinosaur: `${dinosaur.name.toLowerCase()}` } }"
269
337
>
270
338
{{ dinosaur.name }}
271
339
</RouterLink >
272
- < / div >
340
+ </span >
273
341
</template >
274
342
```
275
343
@@ -341,7 +409,7 @@ Then update the `Dinosaur.vue` file:
341
409
},
342
410
async mounted () {
343
411
const res = await fetch (
344
- ` http://localhost:8000 /dinosaurs/${ this .dinosaur } ` ,
412
+ ` /api /dinosaurs/${ this .dinosaur } ` ,
345
413
);
346
414
this .dinosaurDetails = await res .json ();
347
415
},
@@ -366,16 +434,72 @@ Now that we've set up the frontend and backend, we can run the app. In your
366
434
terminal, run the following command:
367
435
368
436
``` shell
369
- deno task dev
437
+ npm run dev
370
438
```
371
439
372
- Visit the output localhost link in your browser to see the app. Click on a
440
+ This will start both the Deno API server on port 8000 and the Vite development
441
+ server on port 3000. The Vite server will proxy API requests to the Deno server.
442
+
443
+ Visit ` http://localhost:3000 ` in your browser to see the app. Click on a
373
444
dinosaur to see more details!
374
445
375
- 
446
+ You can see a live version of the app on
447
+ [ Deno Deploy] ( https://tutorial-with-vue.deno.deno.net/ ) .
448
+
449
+ [ The vue app in action] ( ./images/how-to/vue/vue.gif )
450
+
451
+ ``` shell
452
+ deno run serve
453
+ ```
454
+
455
+ This will build the Vue app and serve everything from the Deno server on
456
+ port 8000.
457
+
458
+ ## Build and deploy
459
+
460
+ We set up the project with a ` serve ` task that builds the React app and serves
461
+ it with the Oak backend server. Run the following command to build and serve the
462
+ app in production mode:
463
+
464
+ ``` sh
465
+ deno run build
466
+ deno run serve
467
+ ```
468
+
469
+ This will:
470
+
471
+ 1 . Build the Vue app using Vite (output goes to ` dist/ ` )
472
+ 2 . Start the Oak server which serves both the API and the built Vue app
473
+
474
+ Visit ` localhost:8000 ` in your browser to see the production version of the app!
475
+
476
+ You can deploy this app to your favorite cloud provider. We recommend using
477
+ [ Deno Deploy] ( https://deno.com/deploy ) for a simple and easy deployment
478
+ experience. You can deploy your app directly from GitHub, simply create a GitHub
479
+ repository and push your code there, then connect it to Deno Deploy.
480
+
481
+ ### Create a GitHub repository
482
+
483
+ [ Create a new GitHub repository] ( https://github.com/new ) , then initialize and
484
+ push your app to GitHub:
485
+
486
+ ``` sh
487
+ git init -b main
488
+ git remote add origin https://github.com/< your_github_username> /< your_repo_name> .git
489
+ git add .
490
+ git commit -am ' my vue app'
491
+ git push -u origin main
492
+ ```
493
+
494
+ ### Deploy to Deno Deploy
495
+
496
+ Once your app is on GitHub, you can deploy it on the Deno Deploy<sup >EA</sup >
497
+ dashboard.
498
+ <a href =" https://app.deno.com/ " class =" docs-cta deploy-cta deploy-button " >Deploy
499
+ my app</a >
500
+
501
+ For a walkthrough of deploying your app, check out the
502
+ [ Deno Deploy tutorial] ( /examples/deno_deploy_tutorial/ ) .
376
503
377
504
🦕 Now that you can run a Vue app in Deno with Vite you're ready to build real
378
- world applications! If you'd like to expand upon this demo you could consider
379
- building out a backend server to serve the static app once built, then you'll be
380
- able to
381
- [deploy your dinosaur app to the cloud](https://docs.deno.com/deploy/manual/).
505
+ world applications!
0 commit comments