-
Notifications
You must be signed in to change notification settings - Fork 382
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
29 changed files
with
1,130 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
This example shows how to use drift declarations across packages. | ||
It is structured as follows: | ||
|
||
- `shared/` contains table definitions. This package does not define a database | ||
on its own (although that could be useful for testing), instead it declares | ||
tables used by the server and the client. | ||
- `server/` is a simple shelf server using Postgres with drift. | ||
- `client/` is a simple CLI client using a local sqlite3 database | ||
while also communicating with the server. | ||
|
||
As the main point of this example is to demonstrate how the build | ||
setup could look like, the client and server are kept minimal. | ||
|
||
To fully build the code, `build_runner run` needs to be run in all three | ||
packages. | ||
However, after making changes to the database code in one of the packages, only | ||
that package needs to be rebuilt. | ||
|
||
## Starting | ||
|
||
To run the server, first start a postgres database server: | ||
|
||
``` | ||
docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres | ||
``` | ||
|
||
Then, run the example by starting a server and a client: | ||
|
||
``` | ||
dart run server/bin/server.dart | ||
dart run client/bin/client.dart | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# https://dart.dev/guides/libraries/private-files | ||
# Created by `dart pub` | ||
.dart_tool/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This file configures the static analysis results for your project (errors, | ||
# warnings, and lints). | ||
# | ||
# This enables the 'recommended' set of lints from `package:lints`. | ||
# This set helps identify many issues that may lead to problems when running | ||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic | ||
# style and format. | ||
# | ||
# If you want a smaller set of lints you can change this to specify | ||
# 'package:lints/core.yaml'. These are just the most critical lints | ||
# (the recommended set includes the core lints). | ||
# The core lints are also what is used by pub.dev for scoring packages. | ||
|
||
include: package:lints/recommended.yaml | ||
|
||
# Uncomment the following section to specify additional rules. | ||
|
||
# linter: | ||
# rules: | ||
# - camel_case_types | ||
|
||
# analyzer: | ||
# exclude: | ||
# - path/to/excluded/files/** | ||
|
||
# For more information about the core and recommended set of lints, see | ||
# https://dart.dev/go/core-lints | ||
|
||
# For additional information about configuring this file, see | ||
# https://dart.dev/guides/language/analysis-options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import 'dart:convert'; | ||
|
||
import 'package:client/database.dart'; | ||
import 'package:drift/drift.dart'; | ||
import 'package:drift/native.dart'; | ||
import 'package:http/http.dart' as http; | ||
import 'package:shared/tables.dart'; | ||
|
||
void main(List<String> arguments) async { | ||
final database = ClientDatabase(NativeDatabase.memory()); | ||
final client = http.Client(); | ||
|
||
// Fetch posts from server and save them in the local database. | ||
final fromServer = | ||
await client.get(Uri.parse('http://localhost:8080/posts/latest')); | ||
|
||
await database.batch((batch) { | ||
final entries = json.decode(fromServer.body) as List; | ||
|
||
for (final entry in entries) { | ||
final post = Post.fromJson(entry['post']); | ||
final user = User.fromJson(entry['author']); | ||
|
||
batch.insert(database.posts, post); | ||
batch.insert(database.users, user, onConflict: DoUpdate((old) => user)); | ||
} | ||
}); | ||
|
||
final localPosts = await database.locallySavedPosts; | ||
print('Saved local posts: $localPosts'); | ||
|
||
await database.close(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
targets: | ||
$default: | ||
builders: | ||
drift_dev: | ||
# disable drift's default builder, we're using the modular setup | ||
# instead. | ||
enabled: false | ||
|
||
# Instead, enable drift_dev:analyzer and drift_dev:modular manually: | ||
drift_dev:analyzer: | ||
enabled: true | ||
options: &options | ||
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/ | ||
store_date_time_values_as_text: true | ||
named_parameters: true | ||
sql: | ||
dialects: | ||
- sqlite | ||
options: | ||
version: "3.45" | ||
modules: [fts5] | ||
drift_dev:modular: | ||
enabled: true | ||
# We use yaml anchors to give the two builders the same options | ||
options: *options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import 'package:drift/drift.dart'; | ||
|
||
import 'database.drift.dart'; | ||
|
||
@DriftDatabase(include: {'package:shared/shared.drift'}) | ||
class ClientDatabase extends $ClientDatabase { | ||
ClientDatabase(super.e); | ||
|
||
@override | ||
int get schemaVersion => 1; | ||
|
||
Future<int> get locallySavedPosts async { | ||
final count = countAll(); | ||
final query = selectOnly(posts)..addColumns([count]); | ||
return query.map((row) => row.read(count)!).getSingle(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// ignore_for_file: type=lint | ||
import 'package:drift/drift.dart' as i0; | ||
import 'package:shared/src/users.drift.dart' as i1; | ||
import 'package:shared/src/posts.drift.dart' as i2; | ||
import 'package:shared/shared.drift.dart' as i3; | ||
import 'package:drift/internal/modular.dart' as i4; | ||
|
||
abstract class $ClientDatabase extends i0.GeneratedDatabase { | ||
$ClientDatabase(i0.QueryExecutor e) : super(e); | ||
late final i1.$UsersTable users = i1.$UsersTable(this); | ||
late final i2.Posts posts = i2.Posts(this); | ||
i3.SharedDrift get sharedDrift => i4.ReadDatabaseContainer(this) | ||
.accessor<i3.SharedDrift>(i3.SharedDrift.new); | ||
@override | ||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables => | ||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>(); | ||
@override | ||
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [users, posts]; | ||
@override | ||
i0.DriftDatabaseOptions get options => | ||
const i0.DriftDatabaseOptions(storeDateTimeAsText: true); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: client | ||
publish_to: none | ||
|
||
environment: | ||
sdk: ^3.2.5 | ||
|
||
dependencies: | ||
drift: ^2.15.0 | ||
http: ^1.2.0 | ||
shared: | ||
path: ../shared/ | ||
|
||
dev_dependencies: | ||
lints: ^2.1.0 | ||
test: ^1.24.0 | ||
drift_dev: ^2.15.0 | ||
build_runner: ^2.4.8 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# https://dart.dev/guides/libraries/private-files | ||
# Created by `dart pub` | ||
.dart_tool/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This file configures the static analysis results for your project (errors, | ||
# warnings, and lints). | ||
# | ||
# This enables the 'recommended' set of lints from `package:lints`. | ||
# This set helps identify many issues that may lead to problems when running | ||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic | ||
# style and format. | ||
# | ||
# If you want a smaller set of lints you can change this to specify | ||
# 'package:lints/core.yaml'. These are just the most critical lints | ||
# (the recommended set includes the core lints). | ||
# The core lints are also what is used by pub.dev for scoring packages. | ||
|
||
include: package:lints/recommended.yaml | ||
|
||
# Uncomment the following section to specify additional rules. | ||
|
||
# linter: | ||
# rules: | ||
# - camel_case_types | ||
|
||
# analyzer: | ||
# exclude: | ||
# - path/to/excluded/files/** | ||
|
||
# For more information about the core and recommended set of lints, see | ||
# https://dart.dev/go/core-lints | ||
|
||
# For additional information about configuring this file, see | ||
# https://dart.dev/guides/language/analysis-options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:drift/drift.dart'; | ||
import 'package:drift_postgres/drift_postgres.dart'; | ||
import 'package:postgres/postgres.dart'; | ||
import 'package:server/database.dart'; | ||
import 'package:shared/tables.dart'; | ||
import 'package:shelf/shelf.dart'; | ||
import 'package:shelf/shelf_io.dart'; | ||
import 'package:shelf_router/shelf_router.dart'; | ||
|
||
// To run this server, first start a local postgres server with | ||
// | ||
// docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres | ||
// | ||
void main(List<String> args) async { | ||
// Use any available host or container IP (usually `0.0.0.0`). | ||
final ip = InternetAddress.anyIPv4; | ||
final database = ServerDatabase(PgDatabase( | ||
endpoint: Endpoint( | ||
host: 'localhost', | ||
database: 'postgres', | ||
username: 'postgres', | ||
password: 'postgres', | ||
), | ||
settings: ConnectionSettings( | ||
// Disable because this example is talking to a local postgres container. | ||
sslMode: SslMode.disable, | ||
), | ||
)); | ||
|
||
final router = Router() | ||
..post('/post', (Request request) async { | ||
final header = request.headers['Authorization']; | ||
if (header == null || !header.startsWith('Bearer ')) { | ||
return Response.unauthorized('Missing Authorization header'); | ||
} | ||
|
||
final user = | ||
await database.authenticateUser(header.substring('Bearer '.length)); | ||
if (user == null) { | ||
return Response.unauthorized('Invalid token'); | ||
} | ||
|
||
database.posts.insertOne(PostsCompanion.insert( | ||
author: user.id, content: Value(await request.readAsString()))); | ||
|
||
return Response(201); | ||
}) | ||
..get('/posts/latest', (req) async { | ||
final somePosts = await database.sharedDrift | ||
.allPosts(limit: (_, __) => Limit(10, null)) | ||
.get(); | ||
|
||
return Response.ok( | ||
json.encode([ | ||
for (final post in somePosts) | ||
{ | ||
'author': post.author, | ||
'post': post.posts, | ||
} | ||
]), | ||
headers: {'Content-Type': 'application/json'}, | ||
); | ||
}); | ||
|
||
// Configure a pipeline that logs requests. | ||
final handler = Pipeline().addMiddleware(logRequests()).addHandler(router); | ||
|
||
// For running in containers, we respect the PORT environment variable. | ||
final port = int.parse(Platform.environment['PORT'] ?? '8080'); | ||
final server = await serve(handler, ip, port); | ||
print('Server listening on port ${server.port}'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
targets: | ||
$default: | ||
builders: | ||
drift_dev: | ||
# disable drift's default builder, we're using the modular setup | ||
# instead. | ||
enabled: false | ||
|
||
# Instead, enable drift_dev:analyzer and drift_dev:modular manually: | ||
drift_dev:analyzer: | ||
enabled: true | ||
options: &options | ||
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/ | ||
store_date_time_values_as_text: true | ||
named_parameters: true | ||
sql: | ||
# The server itself will only support postgres | ||
dialects: | ||
- postgres | ||
options: | ||
version: "3.45" | ||
modules: [fts5] | ||
drift_dev:modular: | ||
enabled: true | ||
# We use yaml anchors to give the two builders the same options | ||
options: *options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import 'package:drift/drift.dart'; | ||
import 'package:shared/tables.dart'; | ||
|
||
import 'database.drift.dart'; | ||
|
||
// Additional table we only need on the server | ||
class ActiveSessions extends Table { | ||
IntColumn get user => integer().references(Users, #id)(); | ||
TextColumn get bearerToken => text()(); | ||
} | ||
|
||
@DriftDatabase( | ||
tables: [ActiveSessions], | ||
include: {'package:shared/shared.drift'}, | ||
) | ||
class ServerDatabase extends $ServerDatabase { | ||
ServerDatabase(super.e); | ||
|
||
@override | ||
int get schemaVersion => 1; | ||
|
||
@override | ||
MigrationStrategy get migration { | ||
return MigrationStrategy( | ||
beforeOpen: (details) async { | ||
if (details.wasCreated) { | ||
await users.insertOne(UsersCompanion.insert(name: 'Demo user')); | ||
await posts.insertOne( | ||
PostsCompanion.insert(author: 1, content: Value('Test post'))); | ||
} | ||
}, | ||
); | ||
} | ||
|
||
Future<User?> authenticateUser(String token) async { | ||
final query = select(users).join( | ||
[innerJoin(activeSessions, activeSessions.user.equalsExp(users.id))]); | ||
query.where(activeSessions.bearerToken.equals(token)); | ||
|
||
final row = await query.getSingleOrNull(); | ||
return row?.readTable(users); | ||
} | ||
} |
Oops, something went wrong.