[pocketbase] A PocketBase backend for Flutter and Dart : Flutter 및 Dart용 PocketBase 백엔드

A step-by-step tutorial

Updated February 14, 2024.

I’ve been looking into solutions for self-hosting a backend server for my Flutter app. In addition to performing normal read-write operations, it also needs to support user authentication. To that end, I’ve been experimenting with ShelfSupabase, and SuperTokens.

Then I discovered PocketBase.

Look at what this Backend as a Service (BaaS) offers:

  • Open source.
  • 30k stars on GitHub and under active development.
  • Has a Dart SDK.
  • Email-password or social auth (Google, Apple, etc).
  • SQLite database, configurable with rules and user roles.
  • Realtime updates.
  • File storage.
  • Admin dashboard.
  • Super easy deployment with a single file.

And that’s just a partial list.

The article that follows will walk you through the steps of connecting your Flutter app to PocketBase. I wrote it partly to teach myself how to do it and partly to teach you. If you still have any questions when you’re finished, please leave a comment. I’ll include some of my own thoughts at the end of the article as well as some reasons you might not choose PocketBase.

Note: This is an intermediate-level tutorial. I’ll assume you already know how to do basic Flutter tasks like building a UI and adding packages.

Getting started

Start by creating a new Flutter project.

Then build a UI like the one in the image below. It should include a Text widget to display the user’s logged-in status and eight buttons titled Sign up, Sign in, Refresh token, Sign out, Create, Read, Update, and Delete:

You’ll use the buttons to learn how to authenticate a user and perform CRUD operations with PocketBase.

Running PocketBase locally

During development, it will be more convenient to have the PocketBase server running on your local machine than on a remote one.

Go to the PocketBase documentation page and download the version for your local machine.

Extract the folder and run the following to start the server:

./pocketbase serve

Note: On Mac, you need to open the folder in Finder, right-click the pocketbase file, and choose Open to bypass the security settings for downloaded files. See more here. After that, run the ./pocketbase serve command again to start the server.

PocketBase will also create a folder named pb_data where it will store the data.

Go to http://127.0.0.1:8090/ in your browser and you should get a 404 response. In this case, that good. It means the server is working.

Creating an admin user

Next, go to http://127.0.0.1:8090/_/ to set up the admin account. You’ll see the following screen:

Fill in the fields and then log in. You’ll be greeted with the following admin dashboard:

You don’t need to do anything now, but keep this browser window open. You’ll come back here later to see the result of the changes you make from your Flutter client.

Adding the PocketBase Dart SDK to Flutter

Although you could communicate with the PocketBase server using its REST API, it will be much easier to directly use the official Dart SDK.

Back in your Flutter app, add the pocketbase package to pubspec.yaml:

dependencies:
  pocketbase: ^0.18.0

You’ll import PocketBase wherever you need it like so:

import 'package:pocketbase/pocketbase.dart';

You’ll also need an instance of PocketBase to connect with the server. Create it wherever it makes sense for your state management approach:

final pb = PocketBase('http://127.0.0.1:8090/');

If you’re doing development using the Android emulator, then use port 10.0.2.2:

final pb = PocketBase('http://10.0.2.2:8090/');

I use the minimalist state management approach. Since I only need a reference to PocketBase in one file for today’s tutorial, I’ll initialize it directly in my state management class. However, if I needed it in multiple places throughout the app, I’d use GetIt.

Signing up a new user

PocketBase makes it very easy to perform the various authentication flows.

Add a method to your Flutter project that will run when the Sign up button is pressed:

Future<void> signUp() async {
  final body = <String, dynamic>{
    "username": "Bob",
    "email": "bob@example.com",
    "password": "12345678",
    "passwordConfirm": "12345678",
    "name": "Bob Smith"
  };

  final record = await pb.collection('users').create(body: body);
  print(record);
}

Here are some notes about that code:

  • pb is the PocketBase instance that you created in the last step.
  • users is the default collection (table) that PocketBase uses for authentication.
  • create adds a new record (row) to the collection. In this case, that means a new user.
  • You pass the email, password, and other parameters as a map to the create body. You can read about the other auth record fields in the docs.

Run the code above and you should see something similar to the response below:

{
  "id":"79yvk2r0lxnt6ob",
  "created":"2024-01-26 03:02:11.967Z",
  "updated":"2024-01-26 03:02:11.967Z",
  "collectionId":"_pb_users_auth_",
  "collectionName":"users",
  "expand":{},
  "avatar":"",
  "emailVisibility":false,
  "name":"Bob Smith",
  "username":"Bob",
  "verified":false
}

Back in the browser dashboard, press the Refresh button and you’ll see that a new user record has been added:

Signing up a new user doesn’t sign them in yet. That’s another step.

Note: I won’t cover it in this tutorial, but before you allow a new user to sign in, you’ll probably want to verify their email. Think about what might happen if you don’t. Some malicious hacker could write a script to register thousands of fake users and then use those accounts to spam your site. PocketBase supports sending email verifications. Here is a video about it. You’ll either want to set up a quality SMTP server or use a third-party email service. Otherwise, your emails will probably be flagged as spam.

Signing in

Next add the code that corresponds with the Sign in button in your app:

Future<void> signIn() async {
  final authData = await pb
      .collection('users')
      .authWithPassword('bob@example.com', '12345678');
  print(authData);
}

authWithPassword sends the email and password for PocketBase to check.

Refresh your app and press the Sign in button. You should see a result similar to the following:

{
  "token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MDc0NDk1OTIsImlkIjoiNzl5dmsycjBseG50Nm9iIiwidHlwZSI6ImF1dGhSZWNvcmQifQ.uee--OI5bagsyVjLHq0kqbrbKQfyQ9UxpVV9g6eR8vc",
  "record":{
    "id":"79yvk2r0lxnt6ob",
    "created":"2024-01-26 03:02:11.967Z",
    "updated":"2024-01-26 03:02:11.967Z",
    "collectionId":"_pb_users_auth_",
    "collectionName":"users",
    "expand":{},
    "avatar":"",
    "email":"bob@example.com",
    "emailVisibility":false,
    "name":"Bob Smith",
    "username":"Bob",
    "verified":false
  },
  "meta":{}
}

That token is a JWT, which if you were to decode, would contain the following information in its payload:

{
  "collectionId": "_pb_users_auth_",
  "exp": 1707449592,
  "id": "79yvk2r0lxnt6ob",
  "type": "authRecord"
}

The exp field is when the token expires. The default duration is two weeks. You can change the default duration in the PocketBase dashboard under Settings > Token Options.

Note: PocketBase doesn’t use refresh tokens. It only uses long-lived access tokens. The reason is that PocketBase is a monolith. The data and the auth all happen on the same server. PocketBase also doesn’t store the token on the server. It just verifies the access tokens that it has previously issued. You can read more here.

After you’ve signed in, you can use the authStore property. This will give you access to token and methods like isValid.

It’s time to update the UI now that the user has signed in. Feel free to use setState or your own preferred state management method. With the minimalist approach, I use Flutter’s built-in ValueNotifier and ValueListenableBuilder.

final statusNotifier = ValueNotifier('Logged out');

Then I update the notifier at the end of the signIn method:

statusNotifier.value = (pb.authStore.isValid) ? 'Logged in' : 'Logged out';

pb.authStore.isValid returns true now that you’ve logged in.

Restart the app and press the Sign in button. Your status text should indicate that you’ve logged in:

Note: Rather than manually updating the notifier, another option would be to listen to the authStore.onChange stream and pair that with a StreamBuilder to update the UI. In a larger app, you could combine this with go_router to automatically redirect to the login screen.

Sign-in is great, but you don’t want to make your users enter their email and password every time they open your app. What you should do is automatically refresh the access token. You’ll learn how to do that in the next step.

Refreshing the token

When you sign in, PocketBase gives you an access token. This token is valid for two weeks by default. It’s your responsibility to manage the token on the Flutter side. If you lose the token or it expires, then you’ll have to ask the user to sign in again.

Add a method that will run when the Refresh token button is pressed:

Future<void> refresh() async {
  if (!pb.authStore.isValid) {
    // TODO: The token has expired. Ask the user to sign in again.
    return;
  }
  final authData = await pb.collection('users').authRefresh();
  print(authData);
}

authRefresh requests a new token with an updated expiration date from PocketBase.

Hot reload your app so that you are still logged in, and then press the Refresh token button. You should get a new JWT token back. If you decode it, you’ll see that the exp expiration timestamp has also been updated.

Persisting the token (optional)

The default AuthStore only keeps the access token in memory, so when you close the app the access token is lost. If you want the user to stay logged in the next time they open your app, you need to provide PocketBase with a custom AuthStore. You can do that with PocketBase’s AsyncAuthStore class.

I’m not going to go into the implementation details of how to store the token string in local storage, but the following code shows how to handle things on the PocketBase side.

Replace your pb instance in your state management class with the following:

late final PocketBase pb;

// 1
Future<void> init() async {
  // 2
  final storage = getIt<LocalStorage>();
  // 3
  final token = await storage.getToken();
  final customAuthStore = AsyncAuthStore(
    initial: token,
    save: storage.setToken,
    clear: storage.deleteToken,
  );
  // 4
  pb = PocketBase(
    'http://$_host:8090/',
    authStore: customAuthStore,
  );
  
  if (pb.authStore.isValid) {
    statusNotifier.value = 'Logged in';
    // 5
    final authRecord = await pb.collection('users').authRefresh();
    print(authRecord);
  } else {
    statusNotifier.value = 'Logged out';
  }
}

String get _host => (Platform.isAndroid) ? '10.0.2.2' : '127.0.0.1';

Here are a few notes:

  1. You should call the init method when the app starts. In the demo project, I called it in the initState method of the home page.
  2. In the demo project, I used get_it to provide a reference to my LocalStorage service. Feel free to use provider or some form of dependency injection.
  3. You can implement LocalStorage any way you like as long as it has a way to save, read and delete the token string. Check out flutter_secure_storage as one option for encrypted storage. In the demo project for this tutorial, I used shared_preferences only for its simplicity. A production app shouldn’t use shared_preferences because it isn’t secure. If a hacker were to get the token, they could do anything the user can do.
  4. Provide your custom auth store to PocketBase when you initialize it.
  5. You can also refresh the auth token when the app first starts up. As you saw earlier, this will update the expiration date, minimizing the chance that the user will need to sign in again. Your custom auth store automatically takes care of saving the token.

Close the app and restart it. You should still be logged in.

Read this and this for a few more pointers on implementing a custom AuthStore.

Signing out

The last authentication-related step this tutorial will cover is signing out. It’s just as easy as the other steps have been.

PocketBase doesn’t store a user’s session data or even their access token on the server. So, in order to log a user out, all you need to do is clear the auth data on the client side.

Add a method that will run when the user presses the Sign out button:

Future<void> signOut() async {
  pb.authStore.clear();
  statusNotifier.value = 'Logged out';
}

That was easy, huh?

Refresh the app and test it out:

There are other auth tasks that you may want to do in the future, such as resetting a forgotten password or requesting an email change. You can check out the PocketBase documentation for details. In fact, if you choose API Preview from the admin dashboard, it will give you code samples of how to do everything.

In the next section, you’ll add a regular collection so that you can perform CRUD operations on it from your Flutter client.

Creating a new collection

In the admin dashboard, click the New collection button:

Adding fields

A new window will pop up. Follow these directions to match the image below:

  • You’ll make a collection to save user exam scores, so write scores for the collection name.
  • There are three different types of collections in PocketBase. Since we want users to be able to edit the contents of this collection, choose Base from the drop-down menu in the top right. (This is the default.)
  • On the Fields tab, click New field to add two new fields.
  • Choose Relation and name it user. Under Select collection, choose the users collection. This will link the user field to the users collection. (It’s like adding a foreign key to a table in SQL.)
  • Click New field again and this time add a Number field. Call it score.

Adding API rules

For security reasons, only admin users can read and write to the collection by default. However, you want to allow the users of your Flutter app to also edit their own data. PocketBase handles this with API Rules.

Click the API Rules tab:

There are five different categories. List/Search rules are for requesting a list of records, while View rules are for requesting a single record. You only want a user to be able to see their own exam scores, not other people’s scores. So for both of these categories, add the following rule:

user = @request.auth.id

This means that the user field of your scores collection must match the authenticated user ID in the request. This effectively filters out all other users.

The same is true for Create and Update. However, you also want to enforce the exam score to be within the range of 0–100. Add the following rule to those two categories:

user = @request.auth.id && 
@request.data.score >= 0 && 
@request.data.score <= 100

This ensures that the score field of the incoming request is within the proper bounds.

Finally, users should only be allowed to delete their own scores, so add the following to the Delete rules:

user = @request.auth.id

When you’re done, save your changes by clicking the Create button in the bottom right corner of the admin dashboard.

Creating a record from Flutter

Back in your Flutter app, add a method that will be called when a user presses the Create button on your Flutter app:

Future<void> create() async {
  final body = <String, dynamic>{
    "user": pb.authStore.model.id,
    "score": 89,
  };

  final record = await pb.collection('scores').create(body: body);
  print(record);
}

You’re again using the create method just like when you added a new user. This time, though, you’re creating a record in the scores collection. Notice that the keys (user and score) of the body map match the field names you chose when you created the collection.

Refresh your app and press the Create button. You should see the following result for record:

{
  "id":"k4oaijxf47jroc0",
  "created":"2024-01-26 09:39:27.934Z",
  "updated":"2024-01-26 09:39:27.934Z",
  "collectionId":"0q5mu8dg1ohw7iz",
  "collectionName":"scores",
  "expand":{},
  "score":89,
  "user":"79yvk2r0lxnt6ob"
}

Great, it’s working.

You need some more user data for the future steps. Let’s create that now. Change the contents of the signUp and signIn methods to add a new user named Mary.

Future<void> signUp() async {
  final body = <String, dynamic>{
    "username": "Mary",             // updated
    "email": "mary@example.com",    // updated
    "password": "12345678",
    "passwordConfirm": "12345678",
    "name": "Mary Smith"            // updated
  };

  final record = await pb.collection('users').create(body: body);
  print(record);
}

Future<void> signIn() async {
  final authData = await pb
      .collection('users')
      .authWithPassword('mary@example.com', '12345678'); // updated
  print(authData);

  statusNotifier.value = (pb.authStore.isValid) ? 'Logged in' : 'Logged out';
}

Now refresh the app and press Sign upSign in, and Create in that order.

Next, change the score in the create method to 92. Then refresh the app and press the Create button again.

Now you should have three records in the scores table: one from Bob and two from Mary. Go to the admin dashboard and press the Refresh button to check it out:

Nice! It worked. You’ve created three new records.

Reading a list of records

Next you’ll try to get a list of all the records for one user. You’re currently logged in as Mary so you would expect to get both of Mary’s exam scores. And if you set up the rules correctly, you shouldn’t get Bob’s score.

Add a method that will be run when the user presses the Read button:

Future<void> read() async {
  final records = await pb.collection('scores').getFullList(
        sort: '-score',
      );
  print(records);
}

Here are a few notes:

  • getFullList returns all of the records in the collection (filtered by the API rules you defined). If that would be too many records, you can paginate the results using getList.
  • sort defines which field you want to sort by, in this case by score. The - dash in front of the field name means that you want to sort in reverse order, in this case from high to low score.
  • There are a number of additional parameters you can include in addition to sort. Others are batchfilter, and fields.

Refresh your app and press the Read button. You should see Mary’s two exam scores:

[
  {
    "id":"m1fkfqdv8r20oif",
    "created":"2024-01-26 09:51:04.343Z",
    "updated":"2024-01-26 09:51:04.343Z",
    "collectionId":"0q5mu8dg1ohw7iz",
    "collectionName":"scores",
    "expand":{},
    "score":92,
    "user":"eokvshdupfla4v4"
  },
  {
    "id":"3fkk1f4sgwqgzje",
    "created":"2024-01-26 09:49:07.460Z",
    "updated":"2024-01-26 09:49:07.460Z",
    "collectionId":"0q5mu8dg1ohw7iz",
    "collectionName":"scores",
    "expand":{},
    "score":89,
    "user":"eokvshdupfla4v4"
  }
]

It’s good to see that Bob (user ID 79yvk2r0lxnt6ob) isn’t there. Your API rule worked. Also, Mary’s scores are sorted from highest to lowest.

There’s a lot more data there than you really need, though. All you want are the score IDs and the scores themselves. You can tell PocketBase which fields you want using the fields parameter of getFullList. Add the following line after sort: ‘-score’,:

fields: 'id,score',

When combining multiple field names, you split them with a comma.

Press the Read button again, and this time, this is what you see:

[
  {
    "id":"m1fkfqdv8r20oif",
    "created":"",
    "updated":"",
    "collectionId":"",
    "collectionName":"",
    "expand":{},
    "score":92
  },
  {
    "id":"3fkk1f4sgwqgzje",
    "created":"",
    "updated":"",
    "collectionId":"",
    "collectionName":"",
    "expand":{},
    "score":89
  }
]

I don’t know that PocketBase needed to give you all the empty fields, but at least you saved it the work of passing in useless values.

Updating a record

Mary isn’t satisfied with her score of 89. She wants a higher grade. In this step, you’ll replace the lowest score with 100.

Create a method that will be called when the Update button is pushed:

Future<void> update() async {
  // Find the record with the lowest score
  final recordList = await pb.collection('scores').getList(
        page: 1,
        perPage: 1,
        skipTotal: true,
        sort: 'score',
        fields: 'id,score',
      );
  print(recordList);
  final record = recordList.items.first;

  // Update the record
  final body = <String, dynamic>{"score": 100};
  final updatedRecord = await pb.collection('scores').update(
        record.id,
        body: body,
      );
  print(updatedRecord);
}

Here are some notes:

  • This time you use getList rather than getFullList because you only need one record.
  • For the same reason, you also only select 1 page and 1 record perPage. You don’t care about the total number of pages or records, so you can skipTotal. All of these are performance optimizations.
  • Once you have the record, you can update the score field using the record ID.

Refresh the app and press the Update button. Here’s what you’ll see:

{
  "page":1,
  "perPage":1,
  "totalItems":-1,
  "totalPages":-1,
  "items":[
    {
      "id":"3fkk1f4sgwqgzje",
      "created":"",
      "updated":"",
      "collectionId":"",
      "collectionName":"",
      "expand":{},
      "score":89
    }
  ]
}

{
  "id":"3fkk1f4sgwqgzje",
  "created":"2024-01-26 09:49:07.460Z",
  "updated":"2024-01-27 04:10:09.986Z",
  "collectionId":"0q5mu8dg1ohw7iz",
  "collectionName":"scores",
  "expand":{},
  "score":100,
  "user":"eokvshdupfla4v4"
}

The score of 89 was updated to 100.

You can also see the same result by refreshing the scores collection in the dashboard:

Hmm, come to think of it, allowing students to update their own exam scores probably isn’t such a great idea. You may want to change the API rules and even introduce user roles like teacher and student.

Deleting a record

The last task you’ll implement in this tutorial is how to delete a record. The only thing you need to know is the record ID.

Add a method that will be called when the user presses the Delete button:

Future<void> delete() async {
  // Find the record with the lowest score
  final recordList = await pb.collection('scores').getList(
        page: 1,
        perPage: 1,
        skipTotal: true,
        sort: 'score',
        fields: 'id,score',
      );
  final record = recordList.items.first;

  // Delete the record
  await pb.collection('scores').delete(record.id);
}

Refresh the app and press the Delete button. There is no return value, but you can refresh the scores collection in the dashboard to see that Mary is missing one value.

That brings you to the end of the tutorial. If you successfully followed along this far, you should have a good idea of how PocketBase works.

Going on

The tutorial didn’t implement any error handling. You can handle that by wrapping the PocketBase calls in try-catch blocks.

Read the Going to Production section of the documentation for how to deploy your server.

Final thoughts

I really like PocketBase. It’s by far the easiest way of setting up a self-hosted auth backend that I’ve found. However, there are a few caveats that I discovered along the way.

  • Currently, bulk create, update, and delete operations are not supported. That means if a user wants to import a lot of data or update or delete many records at once, the only way to do it is by handling one record at a time. If you need to do that for thousands of records, it could be a deal breaker. There is an issue open on GitHub for this topic, but the solution is apparently non-trivial and currently the status is On Hold in the 1.0 roadmap.
  • It’s possible to extend PocketBase and do things like define additional API routes. However, you have to use Go or JavaScript. Unfortunately, Dart isn’t an option.

Both of these drawbacks make me wonder, what if I just used PocketBase as the auth server and ran a Dart Shelf server on the same machine to handle database operations and other logic? It’s not quite as ideal, but I think it could work. To do this, the Flutter client app would first authenticate with PocketBase and get an access token. Then the client would pass the token in the Authorization header to the Dart server. The Dart server would verify the token with PocketBase and if valid would proceed to perform whatever task the user requested. This functionality isn’t designed into PocketBase, but the author acknowledges that it is possible to verify a token by calling authRefresh. Read this GitHub discussion for more.

Update: I’ve written a tutorial about using PostgreSQL on a Dart server. The only other step would be to authenticate the calls with a PocketBase access token. Read that article here: Using PostgreSQL on a Dart server.

 

 

단계별 튜토리얼

2024년 2월 14일에 업데이트되었습니다.

저는 Flutter 앱용 백엔드 서버를 자체 호스팅하기 위한 솔루션을 찾고 있었습니다. 일반적인 읽기-쓰기 작업을 수행하는 것 외에도 사용자 인증도 지원해야 합니다. 이를 위해 저는 Shelf , Supabase 및 SuperTokens 를 실험해 왔습니다 .

그러다가 PocketBase를 발견했습니다 .

이 BaaS(Backend as a Service)가 제공하는 기능을 살펴보세요.

  • 오픈 소스.
  • GitHub에 별 3만 개가 있고 활발하게 개발 중입니다.
  • Dart SDK 가 있습니다 .
  • 이메일 비밀번호 또는 소셜 인증(Google, Apple 등).
  • 규칙 및 사용자 역할로 구성 가능한 SQLite 데이터베이스.
  • 실시간 업데이트.
  • 파일 저장.
  • 관리 대시보드.
  • 단일 파일로 매우 쉽게 배포할 수 있습니다.

그리고 그것은 단지 부분적인 목록일 뿐입니다.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

다음 문서에서는 Flutter 앱을 PocketBase에 연결하는 단계를 안내합니다. 부분적으로는 나 자신에게 그것을 수행하는 방법을 가르치기 위해 썼고 부분적으로는 여러분에게 가르치기 위해 썼습니다. 완료 후에도 궁금한 점이 있으시면 댓글로 남겨주세요. 기사 마지막 부분에 내 생각과 PocketBase를 선택하지 않는 몇 가지 이유를 포함하겠습니다.

참고 : 이 튜토리얼은 중급 수준의 튜토리얼입니다. UI 구축 및 패키지 추가와 같은 기본적인 Flutter 작업을 수행하는 방법을 이미 알고 있다고 가정하겠습니다.

시작하기

새로운 Flutter 프로젝트를 만드는 것부터 시작해 보세요.

그런 다음 아래 이미지와 같은 UI를 빌드합니다. 여기 Text에는 사용자의 로그인 상태를 표시하는 위젯과 가입, 로그인, 토큰 새로 고침, 로그아웃, 만들기, 읽기, 업데이트 및 삭제라는 제목의 8개 버튼이 포함되어야 합니다 .

버튼을 사용하여 사용자를 인증하고 PocketBase로 CRUD 작업을 수행하는 방법을 알아봅니다.

PocketBase를 로컬에서 실행하기

개발 중에는 원격 컴퓨터보다 로컬 컴퓨터에서 PocketBase 서버를 실행하는 것이 더 편리합니다.

PocketBase 설명서 페이지 로 이동하여 로컬 컴퓨터에 맞는 버전을 다운로드하세요.

폴더를 추출하고 다음을 실행하여 서버를 시작합니다.

./pocketbase 서브

참고 : Mac에서는 Finder에서 폴더를 열고 Pocketbase 파일을 마우스 오른쪽 버튼으로 클릭한 다음 열기를 선택하여 다운로드한 파일에 대한 보안 설정을 우회해야 합니다. 여기에서 자세한 내용을 확인하세요 . 그런 다음 ./pocketbase serve명령을 다시 실행하여 서버를 시작하십시오.

pb_dataPocketBase는 또한 데이터를 저장할 폴더를 생성합니다 .

브라우저에서 http://127.0.0.1:8090/ 으로 이동하면 404 응답을 받아야 합니다. 이 경우에는 좋습니다. 서버가 작동 중이라는 의미입니다.

관리자 생성

다음으로 http://127.0.0.1:8090/_/ 로 이동하여 관리자 계정을 설정하세요. 다음 화면이 표시됩니다.

필드를 입력한 후 로그인하면 다음 관리 대시보드가 ​​나타납니다.

지금은 아무것도 할 필요가 없지만 이 브라우저 창을 열어 두세요. 나중에 여기로 돌아와 Flutter 클라이언트에서 변경한 결과를 확인하게 됩니다.

Flutter에 PocketBase Dart SDK 추가

REST API를 사용하여 PocketBase 서버와 통신할 수 있지만 공식 Dart SDK를 직접 사용하는 것이 훨씬 더 쉽습니다.

Flutter 앱으로 돌아가서 pubspec.yamlpocketbase 에 패키지를 추가하세요 .

종속성: 
  포켓베이스:  ^0.18.0

다음과 같이 필요할 때마다 PocketBase를 가져올 수 있습니다.

import  '패키지:pocketbase/pocketbase.dart' ;

PocketBase서버에 연결하려면 인스턴스도 필요합니다 . 상태 관리 접근 방식에 적합한 곳이면 어디든 생성하세요.

최종 pb = PocketBase( 'http://127.0.0.1:8090/' );

Android 에뮬레이터를 사용하여 개발을 수행하는 경우 포트 10.0.2.2를 사용하십시오.

최종 pb = PocketBase( 'http://10.0.2.2:8090/' );

나는 최소한의 상태 관리 접근 방식을 사용합니다 . 오늘 튜토리얼에서는 하나의 파일에 PocketBase에 대한 참조만 필요하므로 상태 관리 클래스에서 직접 초기화하겠습니다. 그러나 앱 전체의 여러 위치에서 필요한 경우 GetIt 을 사용합니다 .

새로운 사용자 등록

PocketBase를 사용하면 다양한 인증 흐름을 매우 쉽게 수행할 수 있습니다.

가입 버튼을 눌렀을 때 실행될 Flutter 프로젝트에 메서드를 추가하세요 .

Future< void > signUp() async { 
  final body = < String , Dynamic >{ 
    "username" : "Bob" , 
    "email" : "bob@example.com" , 
    "password" : "12345678" , 
    "passwordConfirm" : "12345678" , 
    "이름" : "밥 스미스"
   }; 

  최종 기록 = wait pb.collection( 'users' ).create(body: body); 
  인쇄 (기록); 
}

다음은 해당 코드에 대한 몇 가지 참고 사항입니다.

  • pbPocketBase마지막 단계에서 생성한 인스턴스 입니다 .
  • usersPocketBase가 인증을 위해 사용하는 기본 컬렉션(테이블)입니다.
  • create컬렉션에 새 레코드(행)를 추가합니다. 이 경우 이는 새로운 사용자를 의미합니다.
  • 이메일, 비밀번호 및 기타 매개변수를 맵으로 본문에 전달합니다 create문서 에서 다른 인증 레코드 필드에 대해 읽을 수 있습니다 .

위의 코드를 실행하면 아래와 비슷한 응답이 표시됩니다.

{ 
  "id" : "79yvk2r0lxnt6ob" , 
  "생성됨" : "2024-01-26 03:02:11.967Z" , 
  "업데이트됨" : "2024-01-26 03:02:11.967Z" , 
  "collectionId" : " _pb_users_auth_" , 
  "collectionName" : "users" , 
  "expand" : { } , 
  "avatar" : "" , 
  "emailVisibility" : false , 
  "name" : "Bob Smith" , 
  "username" : "Bob" , 
  "확인됨" " : 거짓
 }

브라우저 대시보드로 돌아가서 새로 고침 버튼을 누르면 새 사용자 레코드가 추가된 것을 볼 수 있습니다.

새로운 사용자를 등록해도 아직 로그인되지는 않습니다. 그것은 또 다른 단계입니다.

참고 : 이 튜토리얼에서는 이에 대해 다루지 않지만 새 사용자의 로그인을 허용하기 전에 해당 사용자의 이메일을 확인하고 싶을 것입니다. 그렇지 않으면 어떤 일이 일어날지 생각해 보세요. 일부 악의적인 해커는 수천 명의 가짜 사용자를 등록하는 스크립트를 작성한 다음 해당 계정을 사용하여 사이트에 스팸을 보낼 수 있습니다. PocketBase는 이메일 확인 전송을 지원합니다. 여기 에 대한 비디오가 있습니다. 고품질 SMTP 서버를 설정하거나 타사 이메일 서비스를 사용하고 싶을 것입니다. 그렇지 않으면 귀하의 이메일이 스팸으로 표시될 수 있습니다.

로그인

다음으로 앱의 로그인 버튼 에 해당하는 코드를 추가하세요 .

Future< void > signIn () async { 
  final authData = wait pb 
      .collection( 'users' ) 
      .authWithPassword( 'bob@example.com' , '12345678' ); 
  인쇄(authData); 
}

authWithPassword확인을 위해 PocketBase에 이메일과 비밀번호를 보냅니다.

앱을 새로 고치고 로그인 버튼을 누르세요. 다음과 유사한 결과가 표시됩니다.

{ 
  "토큰" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MDc0NDk1OTIsImlkIjoiNzl5dmsycjBseG50Nm9iIiwid HlwZSI6ImF1dGhSZWNvcmQifQ.uee--OI5bagsyVjLHq0kqbrbKQfyQ9UxpVV9g6eR8vc" , 
  "record" : { 
    "id" : "79yvk2r0lxnt6ob" , 
    "created" : "2024-01-26 03:02:11.967Z" , 
    "up 데이트" : "2024-01-26 03:02:11.967Z" , 
    "collectionId" : "_pb_users_auth_" , 
    "collectionName" : "users" , 
    "expand" : { } , 
    "avatar" : "" , 
    "email" : " bob@example.com" , 
    "emailVisibility" : false , 
    "name" : "Bob Smith" , 
    "username" : "Bob" , 
    "verified" : false
   } , 
  "meta" : { } 
}

해당 토큰은 JWT이며, 이를 디코딩 하려는 경우 페이로드에 다음 정보가 포함됩니다.

{ 
  "collectionId" :  "_pb_users_auth_" , 
  "exp" :  1707449592 , 
  "id" :  "79yvk2r0lxnt6ob" , 
  "type" :  "authRecord" 
}

필드 exp는 토큰이 만료되는 시점입니다. 기본 기간은 2주입니다. PocketBase 대시보드의 설정 > 토큰 옵션 아래에서 기본 기간을 변경할 수 있습니다 .

참고 : PocketBase는 새로 고침 토큰을 사용하지 않습니다. 수명이 긴 액세스 토큰만 사용합니다. 그 이유는 PocketBase가 단일체이기 때문입니다. 데이터와 인증은 모두 동일한 서버에서 발생합니다. PocketBase는 또한 서버에 토큰을 저장하지 않습니다. 이전에 발급한 액세스 토큰만 확인합니다. 여기에서 자세한 내용을 읽을 수 있습니다 .

로그인 후 해당 authStore숙소를 이용하실 수 있습니다. 이를 통해 token다음과 같은 메소드 에 액세스할 수 있습니다 isValid.

이제 사용자가 로그인했으므로 UI를 업데이트할 차례입니다. setState원하는 상태 관리 방법을 자유롭게 사용하세요. 미니멀리스트 접근 방식 으로 Flutter의 내장 ValueNotifier및 ValueListenableBuilder.

final statusNotifier = ValueNotifier( '로그아웃' );

그런 다음 메서드 끝에서 알리미를 업데이트합니다 signIn.

statusNotifier.value = (pb.authStore.isValid) ? '로그인' : '로그아웃' ;

pb.authStore.isValidtrue이제 로그인했으므로 돌아옵니다 .

앱을 다시 시작하고 로그인 버튼을 누르세요. 상태 텍스트에 로그인했음이 표시되어야 합니다.

참고 : 알리미를 수동으로 업데이트하는 대신 다른 옵션은 authStore.onChange스트림을 수신하고 이를 와 페어링하여 StreamBuilderUI를 업데이트하는 것입니다. 더 큰 앱에서는 이것을 go_router 와 결합하여 자동으로 로그인 화면으로 리디렉션할 수 있습니다.

로그인은 훌륭하지만 사용자가 앱을 열 때마다 이메일과 비밀번호를 입력하도록 하고 싶지는 않습니다. 당신이 해야 할 일은 액세스 토큰을 자동으로 새로 고치는 것입니다. 다음 단계에서 그 방법을 배우게 됩니다.

토큰 새로 고침

로그인하면 PocketBase가 액세스 토큰을 제공합니다. 이 토큰은 기본적으로 2주 동안 유효합니다. Flutter 측에서 토큰을 관리하는 것은 귀하의 책임입니다. 토큰을 분실하거나 만료된 경우 사용자에게 다시 로그인하도록 요청해야 합니다.

새로 고침 토큰 버튼을 눌렀을 때 실행될 메서드를 추가합니다 .

Future< void > Refresh() async { 
  if (!pb.authStore.isValid) { 
    // TODO: 토큰이 만료되었습니다. 사용자에게 다시 로그인하도록 요청하세요. 
    반품 ; 
  } 
  final authData = pb.collection( 'users' ).authRefresh()를 기다립니다 . 인쇄 (authData); }
  

authRefreshPocketBase에서 만료 날짜가 업데이트된 새 토큰을 요청합니다.

여전히 로그인되어 있도록 앱을 핫 리로드한 다음 토큰 새로 고침 버튼을 누르세요. 새로운 JWT 토큰을 돌려받아야 합니다. 디코딩 하면 exp만료 타임스탬프도 업데이트된 것을 확인할 수 있습니다 .

토큰 유지(선택 사항)

기본값은 AuthStore액세스 토큰을 메모리에만 유지하므로 앱을 닫으면 액세스 토큰이 손실됩니다. 사용자가 다음에 앱을 열 때 로그인 상태를 유지하려면 사용자 PocketBase정의 AuthStore. PocketBase의 클래스를 사용하면 그렇게 할 수 있습니다 AsyncAuthStore.

토큰 문자열을 로컬 저장소에 저장하는 방법에 대한 구현 세부 사항은 다루지 않겠습니다. 그러나 다음 코드는 PocketBase 측에서 작업을 처리하는 방법을 보여줍니다.

pb상태 관리 클래스의 인스턴스를 다음으로 바꾸십시오 .

후기  최종 PocketBase pb; 

// 1
 Future< void > init() async { 
  // 2 
  최종 저장소 = getIt<LocalStorage>(); 
  // 3 
  최종 토큰 = wait Storage.getToken(); 
  final customAuthStore = AsyncAuthStore( 
    초기: 토큰, 
    저장: Storage.setToken, 
    지우기: Storage.deleteToken, 
  ); 
  // 4
   pb = PocketBase( 
    'http:// $_host :8090/' , 
    authStore: customAuthStore, 
  ); 
  
  if (pb.authStore.isValid) { 
    statusNotifier.value = '로그인됨' ; 
    // 5 
    최종 authRecord = wait pb.collection( 'users' ).authRefresh(); 
    인쇄 (authRecord); 
  } else { 
    statusNotifier.value = '로그아웃됨' ; 
  } 
} 

문자열  get _host => (Platform.isAndroid) ? '10.0.2.2' : '127.0.0.1' ;

다음은 몇 가지 참고 사항입니다.

  1. init앱이 시작될 때 메서드를 호출해야 합니다 . 데모 프로젝트에서는 initState홈페이지의 방식 으로 호출했습니다 .
  2. 데모 프로젝트에서는 get_it내 서비스에 대한 참조를 제공하곤 했습니다 LocalStorage. 자유롭게 사용하거나 provider어떤 형태로든 의존성 주입을 사용하세요.
  3. LocalStorage토큰 문자열을 저장하고 읽고 삭제할 수 있는 방법만 있으면 원하는 방식으로 구현할 수 있습니다 . flutter_secure_storage암호화된 저장 옵션 중 하나로 확인해 보세요 . 이 튜토리얼의 데모 프로젝트에서는 shared_preferences단순성을 위해서만 사용했습니다. 프로덕션 앱은 shared_preferences안전하지 않기 때문에 사용하면 안 됩니다. 해커가 토큰을 얻으려면 사용자가 할 수 있는 모든 작업을 수행할 수 있습니다.
  4. PocketBase초기화할 때 사용자 정의 인증 저장소를 제공하세요 .
  5. 앱이 처음 시작될 때 인증 토큰을 새로 고칠 수도 있습니다. 앞에서 본 것처럼 만료 날짜가 업데이트되어 사용자가 다시 로그인해야 할 가능성이 최소화됩니다. 사용자 정의 인증 저장소는 토큰 저장을 자동으로 처리합니다.

앱을 닫았다가 다시 시작하세요. 아직 로그인되어 있어야 합니다.

사용자 정의 구현에 대한 몇 가지 추가 지침은 이 내용 과 이 내용을 읽어보세요 AuthStore.

로그아웃

이 튜토리얼에서 다룰 마지막 인증 관련 단계는 로그아웃입니다. 다른 단계와 마찬가지로 쉽습니다.

PocketBase는 사용자의 세션 데이터나 액세스 토큰을 서버에 저장하지 않습니다. 따라서 사용자를 로그아웃하려면 클라이언트 측에서 인증 데이터를 지우기만 하면 됩니다.

사용자가 로그아웃 버튼을 눌렀을 때 실행될 메서드를 추가합니다 .

Future< void > signOut () async { 
  pb.authStore.clear(); 
  상태알림자.  = '로그아웃됨' ; 
}

그거 쉬웠지?

앱을 새로 고치고 테스트해 보세요.

잊어버린 비밀번호를 재설정하거나 이메일 변경을 요청하는 등 나중에 수행할 수 있는 다른 인증 작업이 있습니다. 자세한 내용은 PocketBase 설명서를 확인하세요 . 실제로 관리 대시보드에서 API 미리보기를 선택하면 모든 작업을 수행하는 방법에 대한 코드 샘플이 제공됩니다.

다음 섹션에서는 Flutter 클라이언트에서 CRUD 작업을 수행할 수 있도록 일반 컬렉션을 추가합니다.

새 컬렉션 만들기

관리 대시보드에서 새 컬렉션 버튼을 클릭합니다.

필드 추가

새 창이 나타납니다. 아래 이미지와 일치하도록 다음 지침을 따르세요.

  • 사용자 시험 점수를 저장하기 위한 컬렉션을 만들 것이므로 컬렉션 이름에 점수를 작성합니다.
  • PocketBase에는 세 가지 유형의 컬렉션이 있습니다. 사용자가 이 컬렉션의 콘텐츠를 편집할 수 있기를 원하므로 오른쪽 상단의 드롭다운 메뉴에서 기본을 선택하세요. (이것이 기본값입니다.)
  • 필드 탭 에서 새 필드를 클릭하여 두 개의 새 필드를 추가합니다.
  • 관계를 선택 하고 이름을 user 로 지정합니다 . 컬렉션 선택 아래에서 사용자 컬렉션을 선택합니다 . 그러면 user필드가 컬렉션에 연결됩니다 users. (SQL의 테이블에 외래 키를 추가하는 것과 같습니다.)
  • 새 필드를 다시 클릭 하고 이번에는 숫자 필드를 추가합니다. 점수 라고 부르세요 .

API 규칙 추가

보안상의 이유로 기본적으로 관리 사용자만 컬렉션을 읽고 쓸 수 있습니다. 그러나 Flutter 앱 사용자가 자신의 데이터를 편집할 수도 있도록 허용하려고 합니다. PocketBase는 API 규칙을 사용하여 이를 처리합니다.

API 규칙 탭을 클릭합니다 .

다섯 가지 카테고리가 있습니다. 목록/검색 규칙은 기록 목록을 요청하기 위한 것이고, 보기 규칙은 단일 기록을 요청하기 위한 것입니다. 사용자가 다른 사람의 점수가 아닌 자신의 시험 점수만 볼 수 있기를 원합니다. 따라서 두 카테고리 모두에 대해 다음 규칙을 추가하세요.

사용자 = @request.auth.id

이는 컬렉션 user의 필드가 scores요청의 인증된 사용자 ID와 일치해야 함을 의미합니다. 이렇게 하면 다른 모든 사용자를 효과적으로 필터링할 수 있습니다.

Create 및 Update 의 경우에도 마찬가지입니다 . 그러나 시험 점수를 0~100 범위 내로 적용하려고 합니다. 두 범주에 다음 규칙을 추가합니다.

사용자 = @request.auth.id && 
@request.data.score >= 0 && 
@request.data.score <= 100

score이렇게 하면 들어오는 요청의 필드가 적절한 범위 내에 있는지 확인됩니다 .

마지막으로, 사용자는 자신의 점수만 삭제할 수 있도록 허용해야 하므로 삭제 규칙에 다음을 추가하세요.

사용자 = @request.auth.id

완료되면 관리 대시보드 오른쪽 하단에 있는 만들기 버튼을 클릭하여 변경 사항을 저장하세요.

Flutter에서 레코드 만들기

Flutter 앱으로 돌아가서 사용자가 Flutter 앱에서 만들기 버튼 을 누를 때 호출될 메서드를 추가하세요 .

Future< void > create() async { 
  final body = < String , dynamic >{ 
    "user" : pb.authStore.model.id, 
    "score" : 89 , 
  }; 

  최종 기록 = 대기 pb.collection( 'scores' ).create(body: body); 
  인쇄 (기록); 
}

create새 사용자를 추가할 때와 마찬가지로 이 방법을 다시 사용하게 됩니다 . 하지만 이번에는 scores컬렉션에 레코드를 생성하고 있습니다. 맵 의 키( user및 )는 컬렉션을 생성할 때 선택한 필드 이름과 일치합니다.scorebody

앱을 새로 고치고 만들기 버튼을 누르세요. 다음에 대해 다음 결과가 표시됩니다 record.

{ 
  "id" : "k4oaijxf47jroc0" , 
  "생성됨" : "2024-01-26 09:39:27.934Z" , 
  "업데이트됨" : "2024-01-26 09:39:27.934Z" , 
  "collectionId" : " 0q5mu8dg1ohw7iz" , 
  "collectionName" : "scores" , 
  "expand" : { } , 
  "score" : 89 , 
  "user" : "79yvk2r0lxnt6ob" 
}

좋습니다. 작동합니다.

향후 단계를 위해서는 더 많은 사용자 데이터가 필요합니다. 이제 만들어 보겠습니다. signUp및 메소드 의 내용을 변경하여 signIn라는 새 사용자를 추가합니다 Mary.

Future< void > signUp() async { 
  final body = < String , Dynamic >{ 
    "username" : "Mary" ,              // 업데이트된 
    "email" : "mary@example.com" ,     // 업데이트된 
    "password" : "12345678 " , 
    "passwordConfirm" : "12345678" , 
    "name" : "Mary Smith"             // 업데이트됨
   }; 

  최종 기록 = wait pb.collection( 'users' ).create(body: body); 
  인쇄 (기록); 
} 

Future< void > signIn() async { 
  final authData = wait pb 
      .collection( 'users' ) 
      .authWithPassword( 'mary@example.com' , '12345678' ); // 업데이트된 
  인쇄 (authData); 

  statusNotifier.value = (pb.authStore.isValid) ? '로그인' : '로그아웃' ; 
}

이제 앱을 새로 고치고 Sign up , Sign in , Create를 순서대로 누르세요.

그런 다음 메서드를 92로 변경합니다 scorecreate그런 다음 앱을 새로 고치고 만들기 버튼을 다시 누릅니다.

이제 점수 테이블에 세 개의 레코드가 있어야 합니다. 하나는 Bob의 레코드이고 두 개는 Mary의 레코드입니다. 관리자 대시보드로 이동하여 새로 고침 버튼을 눌러 확인하세요.

멋진! 효과가 있었습니다. 세 개의 새 레코드를 만들었습니다.

레코드 목록 읽기

다음으로 한 사용자에 대한 모든 레코드 목록을 가져오려고 합니다. 현재 Mary로 로그인되어 있으므로 Mary의 시험 점수를 모두 얻을 것으로 예상됩니다. 그리고 규칙을 올바르게 설정했다면 Bob의 점수를 받아서는 안 됩니다.

사용자가 읽기 버튼 을 눌렀을 때 실행될 메서드를 추가합니다 .

Future< void > read () async { 
  final records = wait pb.collection( 'scores' ).getFullList( 
        sort: '-score' , 
      ); 
  인쇄(기록); 
}

다음은 몇 가지 참고 사항입니다.

  • getFullList컬렉션의 모든 레코드를 반환합니다(정의한 API 규칙으로 필터링됨). 레코드가 너무 많으면 를 사용하여 결과에 페이지를 매길 수 있습니다 getList.
  • sort정렬할 필드를 정의합니다. 이 경우에는 을 기준으로 합니다 score. 필드 이름 앞의 대시 -는 역순으로(이 경우 높은 점수에서 낮은 점수 순으로) 정렬한다는 의미입니다.
  • 이외에도 포함할 수 있는 추가 매개변수가 많이 있습니다 sort. 다른 것들은 batchfilter및 입니다 fields.

앱을 새로 고치고 읽기 버튼을 누르세요. Mary의 두 가지 시험 점수가 표시됩니다.

[ 
  { 
    "id" : "m1fkfqdv8r20oif" , 
    "생성됨" : "2024-01-26 09:51:04.343Z" , 
    "업데이트됨" : "2024-01-26 09:51:04.343Z" , 
    "collectionId" : "0q5mu8dg1ohw7iz" , 
    "collectionName" : "scores" , 
    "expand" : { } , 
    "score" : 92 , 
    "user" : "eokvshdupfla4v4" 
  } , 
  { 
    "id" : "3fkk1f4sgwqgzje" , 
    "created" : "2024- 01-26 09:49:07.460Z" , 
    "updated" : "2024-01-26 09:49:07.460Z" , 
    "collectionId" : "0q5mu8dg1ohw7iz" , 
    "collectionName" : "점수" , 
    "expand" : { } , 
    "점수" : 89 , 
    "사용자" : "eokvshdupfla4v4" 
  } 
]

79yvk2r0lxnt6obBob(사용자 ID )이 거기에 없다는 것을 보니 반갑습니다 . API 규칙이 작동했습니다. 또한 Mary의 점수는 높은 점수에서 낮은 점수로 정렬됩니다.

하지만 실제로 필요한 것보다 훨씬 더 많은 데이터가 있습니다. 원하는 것은 점수 ID와 점수 자체뿐입니다. fields의 매개변수를 사용하여 PocketBase에 원하는 필드를 알릴 수 있습니다 getFullList. 뒤에 다음 줄을 추가합니다 sort: ‘-score’,.

필드: 'id,score' ,

여러 필드 이름을 결합할 때는 쉼표로 구분합니다.

읽기 버튼을 다시 누르면 이번에는 다음과 같은 내용이 표시됩니다.

[ 
  { 
    "id" : "m1fkfqdv8r20oif" , 
    "created" : "" , 
    "updated" : "" , 
    "collectionId" : "" , 
    "collectionName" : "" , 
    "expand" : { } , 
    "score" : 92 
  } , 
  { 
    "id" : "3fkk1f4sgwqgzje" , 
    "created" : "" , 
    "updated" : "" , 
    "collectionId" : "" , 
    "collectionName" : "" , 
    "expand" : { } , 
    "score" : 89 
  } 
]

PocketBase가 빈 필드를 모두 제공해야 했는지는 모르겠지만 적어도 쓸모없는 값을 전달하는 작업을 저장했습니다.

기록 업데이트

Mary는 89점에 만족하지 않습니다. 그녀는 더 높은 성적을 원합니다. 이 단계에서는 가장 낮은 점수를 100으로 바꿉니다.

업데이트 버튼을 눌렀을 때 호출될 메서드를 만듭니다 .

Future< void > update () async { 
  // 점수가 가장 낮은 레코드를 찾습니다. 
  final RecordList = wait pb. 컬렉션 ( '점수' ). getList ( 
        페이지 : 1 , 
        perPage : 1 , 
        SkipTotal : true , 
        sort : 'score' , 
        fields : 'id,score' , 
      ); 
  인쇄 (recordList); 
  최종 기록 = RecordList.items.first; 

  // 레코드 업데이트 
  final body = <String, Dynamic>{ "score" : 100 }; 
  최종 업데이트된 레코드 = pb를 기다립니다. 컬렉션 ( '점수' ). 업데이트 ( 
        record.id, 
        body : body, 
      ); 
  인쇄 (updatedRecord); 
}

다음은 몇 가지 참고 사항입니다.

  • 이번에는 레코드가 하나만 필요하기 때문에 사용하는 getList것이 아닙니다 .getFullList
  • 같은 이유로 1개와 page1개의 레코드 만 선택합니다 perPage. 총 페이지나 레코드 수는 신경 쓰지 않으셔도 됩니다 skipTotal. 이 모든 것은 성능 최적화입니다.
  • 레코드가 있으면 score레코드 ID를 사용하여 필드를 업데이트할 수 있습니다.

앱을 새로 고치고 업데이트 버튼을 누르세요. 표시되는 내용은 다음과 같습니다.

{ 
  "page" : 1 , 
  "perPage" : 1 , 
  "totalItems" :- 1 , 
  "totalPages" :- 1 , 
  "items" : [ 
    { 
      "id" : "3fkk1f4sgwqgzje" , 
      "created" : "" , 
      "업데이트됨 " : "" , 
      "collectionId" : "" , 
      "collectionName" : "" , 
      "expand" : {}, 
      "score" : 89
     } 
  ] 
} 

{ 
  "id" : "3fkk1f4sgwqgzje" , 
  "created" : "2024- 01-26 09:49:07.460Z" , 
  "updated" : "2024-01-27 04:10:09.986Z" , 
  "collectionId" : "0q5mu8dg1ohw7iz" , 
  "collectionName" : "점수" , 
  "expand" : { }, 
  "점수" : 100 , 
  "사용자" : "eokvshdupfla4v4"
 }

89점이었던 점수가 100점으로 업데이트되었습니다.

scores대시보드에서 컬렉션을 새로 고쳐도 동일한 결과를 볼 수도 있습니다 .

흠, 생각해보면 학생들이 자신의 시험 점수를 업데이트할 수 있도록 하는 것은 그다지 좋은 생각이 아닐 것입니다. API 규칙을 변경하고 및 같은 사용자 역할을 도입 할 수도 있습니다 .teacherstudent

기록 삭제

이 자습서에서 구현할 마지막 작업은 레코드를 삭제하는 방법입니다. 알아야 할 유일한 것은 레코드 ID입니다.

사용자가 삭제 버튼 을 누를 때 호출될 메서드를 추가합니다 .

Future< void > delete() async { 
  // 점수가 가장 낮은 레코드 찾기 
  final RecordList = wait pb.collection( 'scores' ).getList( 
        page: 1 , 
        perPage: 1 , 
        skiTotal: true , 
        sort: 'score' , 
        필드: 'id,score' , 
      ); 
  최종 기록 = RecordList.items.first; 

  // 레코드 삭제 
  wait pb.collection( 'scores' ).delete(record.id); 
}

앱을 새로 고치고 삭제 버튼을 누르세요. 반환 값은 없지만 scores대시보드에서 컬렉션을 새로 고쳐 Mary에 값 하나가 누락되었는지 확인할 수 있습니다.

이로써 튜토리얼이 종료됩니다. 여기까지 성공적으로 따라오셨다면 PocketBase가 어떻게 작동하는지 잘 이해하셨을 것입니다.

계속

튜토리얼에서는 오류 처리를 구현하지 않았습니다. PocketBase 호출을 try-catch블록으로 래핑하여 이를 처리할 수 있습니다.

서버 배포 방법에 대해서는 설명서의 프로덕션 시작 섹션을 읽어보세요 .

마지막 생각들

저는 PocketBase를 정말 좋아합니다. 제가 찾은 자체 호스팅 인증 백엔드를 설정하는 가장 쉬운 방법입니다. 그러나 그 과정에서 발견한 몇 가지 주의 사항이 있습니다.

  • 현재 대량 생성, 업데이트 및 삭제 작업은 지원되지 않습니다. 즉, 사용자가 한 번에 많은 데이터를 가져오거나 많은 레코드를 업데이트 또는 삭제하려는 경우 이를 수행할 수 있는 유일한 방법은 한 번에 하나의 레코드를 처리하는 것입니다. 수천 개의 레코드에 대해 그렇게 해야 한다면 거래가 중단될 수 있습니다. 이 주제에 대해 GitHub에 공개된 문제가 있지만 해결 방법은 명백히 중요하지 않으며 현재 1.0 로드맵 에서 상태는 보류 중입니다 .
  • PocketBase를 확장 하고 추가 API 경로를 정의하는 등의 작업을 수행하는 것이 가능합니다 . 단, Go나 JavaScript를 사용해야 합니다. 불행히도 Dart는 선택 사항이 아닙니다.

이러한 두 가지 단점으로 인해 PocketBase를 인증 서버로 사용하고 동일한 시스템에서 Dart Shelf 서버를 실행하여 데이터베이스 작업 및 기타 논리를 처리하면 어떻게 될까요? 그다지 이상적이지는 않지만 작동할 수 있다고 생각합니다. 이를 위해 Flutter 클라이언트 앱은 먼저 PocketBase로 인증하고 액세스 토큰을 얻습니다. 그런 다음 클라이언트는 헤더의 토큰을 AuthorizationDart 서버에 전달합니다. Dart 서버는 PocketBase로 토큰을 확인하고 유효한 경우 사용자가 요청한 모든 작업을 계속 수행합니다. 이 기능은 PocketBase에 설계되지 않았지만 작성자는 authRefresh. 자세한 내용은 이 GitHub 토론을 읽어보세요 .

업데이트 : 저는 Dart 서버에서 PostgreSQL을 사용하는 방법에 대한 튜토리얼을 작성했습니다. 유일한 다른 단계는 PocketBase 액세스 토큰을 사용하여 호출을 인증하는 것입니다. 여기에서 해당 기사를 읽어보세요: Dart 서버에서 PostgreSQL 사용하기 .

[출처] https://suragch.medium.com/a-pocketbase-backend-for-flutter-and-dart-c962bea4e3f1

 

 

 

 

 

 

 

 

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
171 [Ren'Py] 렌파이(RenPy) 에 광고 (애드몹) 붙이기 file 졸리운_곰 2024.12.20 225
170 [godot 엔진] 오픈 소스 고도 프로젝트 모음 - 고도 배우기, Godot file 졸리운_곰 2024.12.16 337
169 [RPG Maker] Simple AdMob - RPG Maker MV 졸리운_곰 2024.12.16 324
168 [RPG Maker] 코르도바 광고 삽입 플러그인 - RPG Maker MV file 졸리운_곰 2024.12.15 160
167 [RPG Maker] MacOS에서 RPG Maker MZ로 만든 게임을 Android 앱으로 빌드하기 file 졸리운_곰 2024.12.15 196
166 [Unity] [모바일, 2D] 유니티 기초2 - 씬 전환하기 file 졸리운_곰 2024.12.14 186
165 [flutter (플루터 앱 개발)] How to Implement Any UI in Flutter : 플러터에서 모든 UI를 구현하는 방법 file 졸리운_곰 2024.11.28 192
164 [게임 일반] SharpMoku a Gomoku/Five in a Row Written in C# : SharpMoku a Gomoku/Five in a Row C#로 작성됨 : 오목게임 개발 file 졸리운_곰 2024.10.31 311
163 [flutter] A PocketBase backend for Flutter and Dart : Flutter 및 Dart용 PocketBase 백엔드 file 졸리운_곰 2024.10.29 266
162 [flutter] 44... 실 기기 테스트용 apk 파일 빌드하기 졸리운_곰 2024.10.26 233
161 [게임 마켓플레이스] 스팀에 ‘게임’을 출시하고 싶다면? file 졸리운_곰 2024.09.07 316
160 [TextMode Game Create] Text based game 졸리운_곰 2024.08.25 519
159 [pocketbase] 포켓 베이스(Pocket Base) 빠르게 입문하기(CRUD) file 졸리운_곰 2024.08.20 342
158 [pocketbase] Flutter 및 Dart용 PocketBase 백엔드 : A PocketBase backend for Flutter and Dart file 졸리운_곰 2024.08.18 394
157 [flutter (플루터 앱 개발)] [플러터] ios : error while build iOS app in Xcode : Sandbox: rsync.samba (13105) deny(1) file-write-create, Flutter failed to write to a file file 졸리운_곰 2024.07.26 296
156 [flutter (플루터 앱 개발)] [플러터] ios 앱 출시하기 file 졸리운_곰 2024.07.24 343
155 [flutter (플루터 앱 개발)] flutter 안드로이드 출시하기 keystore 적용 (keystore 분실 해결방법) 졸리운_곰 2024.07.16 333
154 [flutter (플루터 앱 개발)] Flutter - GetX를 이용한 상태관리 file 졸리운_곰 2024.07.12 326
» [pocketbase] A PocketBase backend for Flutter and Dart : Flutter 및 Dart용 PocketBase 백엔드 file 졸리운_곰 2024.06.19 283
152 [flutter (플루터 앱 개발)] flutter pocketbase 사용법 3탄(로그인) file 졸리운_곰 2024.06.10 413
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED