1 | # Wander
|
2 |
|
3 | Database Migrations for Modern Apps
|
4 |
|
5 | # Introduction
|
6 |
|
7 | __Wander__ is a command-line tool that enables programmable database changes. Through __Migrations__, you can version control your database and easily `commit` or `revert` changes between versions.
|
8 |
|
9 | # Supported Databases
|
10 |
|
11 | Currently, only `mysql` is supported. Adapters for other databases are still under development.
|
12 |
|
13 | + ✓ MySQL
|
14 | + ✗ PostgreSQL
|
15 | + ✗ SQL Server
|
16 | + ✗ Oracle DB
|
17 |
|
18 | # Getting Started
|
19 |
|
20 | To get started, globally install `wander-cli`
|
21 |
|
22 | ```bash
|
23 | > npm install -g wander-cli
|
24 | ```
|
25 |
|
26 | # Initializing Wander
|
27 |
|
28 | To initialize a new `wander` project, run `wander init` inside your project folder
|
29 |
|
30 | ```bash
|
31 | > mkdir MyDatabase
|
32 | > cd MyDatabase
|
33 | > wander init
|
34 | ```
|
35 |
|
36 | This will ask you for the name of your `migrations` folder (default: `./migrations`) and your database URI. Once completed, you can now start using wander.
|
37 |
|
38 | # Creating a new Migration
|
39 |
|
40 | To create a new migration, run `wander new <name>`.
|
41 |
|
42 | ```bash
|
43 | > wander new create_post
|
44 | ```
|
45 |
|
46 | This will create a new file inside your `migrations` folder
|
47 |
|
48 | ```markdown
|
49 | + migrations
|
50 | - v1_0_0__create_post.js
|
51 | + wrconfig.json
|
52 | + .gitignore
|
53 | ```
|
54 |
|
55 | > Note that you can change the type of version that your new migration will use, using the `-t` or `--version-type` option.
|
56 |
|
57 | ```
|
58 | {major}.{minor}.{patch}
|
59 | ```
|
60 |
|
61 | __Version Commands:__
|
62 | + major: `wander new create_post -t major` (e.g. 2.0.0)
|
63 | + minor: `wander new crete_post -t minor` (e.g. 1.1.0)
|
64 | + patch: `wander new create_posrt -t patch` (e.g. 1.0.1)
|
65 |
|
66 | Once created, the file would look similar to the following.
|
67 |
|
68 | **v1_0_0__create_post.js**
|
69 | ```javascript
|
70 | module.exports = {
|
71 | version() {
|
72 | return '1.0.0';
|
73 | },
|
74 | description() {
|
75 | return `Create post`;
|
76 | },
|
77 | async up({}) {
|
78 | },
|
79 | async down({}) {
|
80 | }
|
81 | };
|
82 | ```
|
83 |
|
84 | __Description__
|
85 |
|
86 | + The `version` method returns the version name of your migration.
|
87 | + The `description` method returns the a brief description of your database changes. You can expand this as you wish.
|
88 | + The `up` method runs whenever the migration is committed
|
89 | + The `down` method runs whenever the migration is reverted
|
90 |
|
91 | For the `up` and `down` methods, an object is passed to them for your usage, which includes serveral useful functions. Check the [API](#api) section for more information.
|
92 |
|
93 | __Example__
|
94 |
|
95 | ```javascript
|
96 | module.exports = {
|
97 | version() {
|
98 | return '1.0.0';
|
99 | },
|
100 | description() {
|
101 | return `Create post`;
|
102 | },
|
103 | async up({ create, seed }) {
|
104 | create('post', table => {
|
105 | table.id();
|
106 | table.string('caption');
|
107 | table.timestamps();
|
108 | });
|
109 |
|
110 | seed('post', [
|
111 | { caption: 'New post!' },
|
112 | { caption: 'Another post!' }
|
113 | ]);
|
114 | },
|
115 | async down({ drop, truncate }) {
|
116 | truncate('post');
|
117 | drop('post');
|
118 | }
|
119 | };
|
120 | ```
|
121 |
|
122 | # API
|
123 |
|
124 | ## create
|
125 |
|
126 | __Parameters__
|
127 |
|
128 | | Paramter | Data type |
|
129 | | ---------- | ---------------------------------- |
|
130 | | tableName | string |
|
131 | | definition | (table: [Table](#table)) => void |
|
132 |
|
133 | __Description__
|
134 |
|
135 | + Accepts a __tableName__ that you want to create, and a definition function.
|
136 | + The definition function has a [Table](#table) parameter that allows you to define the structure of your table.
|
137 |
|
138 | __Example__
|
139 |
|
140 | ```javascript
|
141 | create('post', table => {
|
142 | table.id();
|
143 | table.string('caption');
|
144 | table.timestamps();
|
145 | });
|
146 | ```
|
147 |
|
148 | ## alter
|
149 |
|
150 | __Parameters__
|
151 |
|
152 | | Paramter | Data type |
|
153 | | ---------- | ---------------------------------------- |
|
154 | | tableName | string |
|
155 | | definition | (table: [Table](#table-class)) => void |
|
156 |
|
157 | __Description__
|
158 |
|
159 | + Accepts a __tableName__ that you want to alter, and a definition function.
|
160 | + The definition function has a [Table](#table-class) parameter that allows you to define the structure of your table.
|
161 |
|
162 | __Example__
|
163 |
|
164 | ```javascript
|
165 | alter('comment', table => {
|
166 | table.integer('comment_count').add();
|
167 | });
|
168 | ```
|
169 |
|
170 | ## drop
|
171 |
|
172 | __Parameters__
|
173 |
|
174 | | Paramter | Data type |
|
175 | | ---------- | ---------------------------------- |
|
176 | | tableName | string |
|
177 |
|
178 | __Description__
|
179 |
|
180 | + Accepts a __tableName__ that you want to drop.
|
181 |
|
182 | __Example__
|
183 |
|
184 | ```javascript
|
185 | drop('comment');
|
186 | ```
|
187 |
|
188 | ## seed
|
189 |
|
190 | __Parameters__
|
191 |
|
192 | | Paramter | Data type |
|
193 | | ---------- | ---------------------------------- |
|
194 | | tableName | string |
|
195 | | seeds | object[] |
|
196 |
|
197 | __Description__
|
198 |
|
199 | + Accepts a __tableName__ that you want to seed with data.
|
200 | + Accepts a __seeds__ array of objects defining the data you want to insert.
|
201 |
|
202 | > __NOTE:__ Only the columns of the first item in the list will define the columns that will be populated for all of the items.
|
203 |
|
204 | __Example__
|
205 |
|
206 | ```javascript
|
207 | seed('post', [
|
208 | { caption: 'A new post!' },
|
209 | { capton: 'Another post' }
|
210 | ]);
|
211 | ```
|
212 |
|
213 | ## truncate
|
214 |
|
215 | __Parameters__
|
216 |
|
217 | | Paramter | Data type |
|
218 | | ---------- | ---------------------------------- |
|
219 | | tableName | string |
|
220 |
|
221 | __Description__
|
222 |
|
223 | + Accepts a __tableName__ that you want to truncate.
|
224 |
|
225 | __Example__
|
226 |
|
227 | ```javascript
|
228 | truncate('comment');
|
229 | ```
|
230 |
|
231 | ## execute
|
232 |
|
233 | __Parameters__
|
234 |
|
235 | | Paramter | Data type |
|
236 | | ---------- | ---------------------------------- |
|
237 | | query | string |
|
238 |
|
239 | __Description__
|
240 |
|
241 | + Ideal for running scripts that are too complex or are too specific to the database you are running.
|
242 | + Accepts a __query__ that you want to execute.
|
243 |
|
244 | > WARNING: Running execute is very dangerous especially if the keys and values you are trying to use are not properly escaped.
|
245 |
|
246 | __Example__
|
247 |
|
248 | ```javascript
|
249 | execute(`
|
250 | SET sql_safe_updates = 0;
|
251 | UPDATE post SET updated_at = now() WHERE id > 5;
|
252 | `);
|
253 | ```
|
254 |
|
255 |
|
256 | # __Table__ class
|
257 |
|
258 | __Description__
|
259 |
|
260 | + Allows you to define the structue of your table via different methods
|
261 |
|
262 | __Methods__
|
263 |
|
264 | + id(`columnName`?: string)
|
265 | - Adds a primary auto_increment key (default: `id`)
|
266 | + string(`columnName`: string, `length`?: number)
|
267 | - Adds a string column (e.g. on MySQL this is equivalent to `varchar`)
|
268 | + text(`columnName`: string)
|
269 | - Adds a text column (unlike string, there is no limit to the length)
|
270 | + integer(`columnName`: string, `length`?: number)
|
271 | - Adds an integer column
|
272 | + float(`columnName`: string, `length`?: number, `precision`?: number)
|
273 | - Adds a float column
|
274 | + decimal(`columnName`: string, `length`?: number, `precision`?: number)
|
275 | - Adds a decimal column
|
276 | + double(`columnName`: string, `length`?: number, `precision`?: number)
|
277 | - Adds a double column
|
278 | + pointer(`tableName`: string, `via`?: string)
|
279 | - Adds an integer column pointing to another table
|
280 | - `via`, by default, is `tableName`_`id`
|
281 | + date(`columnName`: string)
|
282 | - Adds a date column
|
283 | + datetime(`columnName`: string)
|
284 | - Adds a datetime column
|
285 | + boolean(`columnName`: string)
|
286 | - Adds a boolean column (e.g. on MySQL this is equivalent to `bit`)
|
287 | + json(`columnName`: string)
|
288 | - Supported in `MySQL 5.7+`
|
289 | + timestamps()
|
290 | - Creates `created_at`, `updated_at` and `deleted_at` datetime columns
|
291 | + index(`columnName`: string, `alias`?: string)
|
292 | - Create an index on the table
|
293 | + unique(`columnName`: string, `alias`?: string)
|
294 | - Create a unique index on the table
|
295 |
|
296 | ## Key Actions
|
297 |
|
298 | In addition, when using the `alter` option, the above methods have specific actions that need to be defined in order to let `wander` know how the columns are going to be changed.
|
299 |
|
300 | + add()
|
301 | - Add the column/index/unique key
|
302 | + modify()
|
303 | - Modify the column
|
304 | + drop()
|
305 | - Drop the column/index/unique key
|
306 |
|
307 | __Example__
|
308 |
|
309 | ```javascript
|
310 | alter('comment', table => {
|
311 | table.integer('comment_count').add();
|
312 | table.index('IDENT').drop();
|
313 | });
|
314 | ```
|
315 |
|
316 | # Sample migration
|
317 |
|
318 | ```javascript
|
319 | module.exports = {
|
320 | version() {
|
321 | return '1.0.0';
|
322 | },
|
323 | description() {
|
324 | return `
|
325 | Created the post table.
|
326 | Changed the comment table.
|
327 | Populated the post table.
|
328 | `;
|
329 | },
|
330 | async up({ create, alter, seed, execute }) {
|
331 | create('post', table => {
|
332 | table.id();
|
333 | table.string('caption', 500);
|
334 | table.pointer('user');
|
335 | table.json('info');
|
336 | table.string('identifier');
|
337 | table.index('identifier', 'IDENT');
|
338 | table.timestamps();
|
339 | });
|
340 |
|
341 | // Equivalent to the following
|
342 | `
|
343 | CREATE TABLE post (
|
344 | id int AUTO_INCREMENT,
|
345 | caption varchar(500),
|
346 | user_id int(11),
|
347 | info json,
|
348 | identifier varchar(30),
|
349 | PRIMARY KEY (id),
|
350 | INDEX IDENT (identifier));
|
351 | `
|
352 |
|
353 | alter('comment', table => {
|
354 | table.integer('like_count').add();
|
355 | table.string('message', 250).modify();
|
356 | table.pointer('post').drop();
|
357 | table.index('IDENT').drop();
|
358 | });
|
359 |
|
360 | // Equivalent to the following
|
361 | `
|
362 | ALTER TABLE comment
|
363 | ADD COLUMN like_count int,
|
364 | MODIFY COLUMN message varchar(250),
|
365 | DROP COLUMN post_id;
|
366 | `
|
367 |
|
368 | seed('post', [
|
369 | { caption: 'New caption!' },
|
370 | { caption: 'New caption!' },
|
371 | { caption: 'New caption!' }
|
372 | ]);
|
373 |
|
374 | // Equivalent to the following
|
375 | `
|
376 | INSERT INTO post
|
377 | (caption)
|
378 | VALUES
|
379 | ('New caption!'),
|
380 | ('New caption!'),
|
381 | ('New caption!');
|
382 | `
|
383 |
|
384 | execute(`
|
385 | SET sql_safe_updates = 0;
|
386 | UPDATE post SET privacy = 'public' WHERE role IS NULL;
|
387 | `);
|
388 | },
|
389 | async down({ alter, drop, truncate }) {
|
390 |
|
391 | truncate('post');
|
392 | drop('post');
|
393 |
|
394 | alter('comment', () => {
|
395 | this.integer('like_count').drop();
|
396 | this.string('message', 500).modify();
|
397 | this.pointer('post').add();
|
398 | this.index('identifier', 'IDENT').add();
|
399 | });
|
400 |
|
401 | execute(`UPDATE post SET privacy = 'private' WHERE role IS NULL`);
|
402 | }
|
403 | };
|
404 | ```
|
405 |
|
406 | # Up versus Down
|
407 |
|
408 | In terms of the `Migrations` standard for databases, the convention is to always have `up` and `down` methods.
|
409 |
|
410 | The purpose of the `up` method is to commit the changes you want to apply to the database. Conversely, it is required that you undo everything that you performed, inside the `down` method.
|
411 |
|
412 | The reason why this practice is observed is that it allows you, to some extent, to recover from a breaking change that was executed accidentally. Also, it allows you to ensure consistency when changing between database versions.
|
413 |
|
414 | Do note, however, that unless a `seed` option is defined inside the methods, any data that was __truncated__ or __dropped__ will be __lost__ forever.
|
415 |
|
416 | # Transactions
|
417 |
|
418 | For `wander`, all migrations are executed as a single transaction. That means the [MigrationOptions](#migrationoptions-object) you used will not be fired until the processor has gone through all of them.
|
419 |
|
420 | That also means that if anything goes wrong, the scripts will be rolled back (except for cases where database auto-commits occur). See the corresponding manuals of your databases for more information. |
\ | No newline at end of file |