ApiInterceptor usage and security guide for Serverpod API request/response logging.
Architecture Overview #
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Repository â
â âââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â ApiInterceptor â â
â â âââââââââââââââââââââââââââââââââââââââââââ â â
â â â Serverpod Client â â â
â â â ( kobic_client â kobic_server) â â â
â â âââââââââââââââââââââââââââââââââââââââââââ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ApiInterceptor Usage #
Location #
package/core/lib/src/data/api_interceptor.dart
Default Settings #
const ApiInterceptor ( {
this . enableLogging = true , // Enable API call logging
this . enableRetry = true , // Enable retry on failure
this . maxRetries = 3 , // Maximum retry count
this . retryDelay = 1s, // Delay between retries
} )
Usage in Repository #
class BookRepository implements IBookRepository {
final ApiInterceptor _interceptor;
@override
Future < Either < Failure , Book >> getBook ( int id) async {
try {
final result = await _interceptor. executeApiCall (
apiCall: ( ) => _client. book. getBook ( id) ,
apiName: 'getBook' ,
parameters: { 'id' : id} ,
) ;
return Right ( result) ;
} on ApiException catch ( error) {
return Left ( error. toFailure ( ) ) ;
}
}
}
API Call Start #
ð API Call : getBook
â° Start : 10 : 30 : 45.123
ð Parameters :
âĒ id: 123
API Success #
â
API Success : getBook ( 245ms)
ð Result : Book
âĒ Value : Book ( id: 123 , title: ' Title ' )
API Failure #
â API Failure : getBook ( 1205ms)
ðĨ Error : ServerpodClientException
ð Message : Not Found
Retry #
â ïļ Retry 2 / 3 ( after 1000ms)
Retry Policy #
Retryable Errors #
Status Code Description Retry
5xx Server error â
408 Request Timeout â
socket error Network connection failure â
timeout Request timeout â
handshake error SSL handshake failure â
4xx (other) Client error â
Custom Retry Settings #
// Custom retry configuration
final interceptor = ApiInterceptor (
enableRetry: true ,
maxRetries: 5 ,
retryDelay: const Duration ( seconds: 2 ) ,
) ;
Error Conversion #
ApiInterceptor converts ServerpodClientException to project custom exceptions.
Status Code Converted Exception Message
401 UnauthorizedExceptionAuthentication required
403 ForbiddenExceptionAccess denied
404 NotFoundExceptionResource not found
422 ValidationExceptionInvalid input data
500 ServerExceptionServer error occurred
socket NetworkExceptionNetwork connection error
timeout TimeoutExceptionRequest timeout
Security Rules #
// â Forbidden: Include sensitive info in parameters
await _interceptor. executeApiCall (
apiCall: ( ) => _client. auth. login ( userId, password) ,
apiName: 'login' ,
parameters: {
'userId' : userId,
'password' : password, // ðĻ Forbidden!
} ,
) ;
// â
Recommended: Exclude sensitive info
await _interceptor. executeApiCall (
apiCall: ( ) => _client. auth. login ( userId, password) ,
apiName: 'login' ,
parameters: { 'userId' : userId} , // password excluded
) ;
Category Examples Logging
Credentials password, pin, userPw â Forbidden
Tokens accessToken, refreshToken, idToken â Forbidden
Identity info userId, loginId (except for debugging) â ïļ Caution
Personal info phoneNumber, email, address â Forbidden
Auth codes smsCode, verificationCode â Forbidden
kDebugMode Pattern #
Disable Logging in Production Builds #
import 'package:flutter/foundation.dart' show kDebugMode ;
// Set in ApiInterceptorConfig
class ApiInterceptorConfig {
static const defaultEnableLogging = kDebugMode;
// ...
}
// Or explicitly set during creation
final interceptor = ApiInterceptor (
enableLogging: kDebugMode,
) ;
kDebugMode Advantages #
Compile-time constant, completely removed from production code
No runtime overhead
Enhanced security (prevents log exposure in production)
Server-side Logging (Serverpod) #
Using session.log() #
// In backend/kobic_server
Future < Book > getBook ( Session session, int id) async {
session . log ( 'ð Book lookup request: id=$ id ' ) ;
final book = await Book . db. findById ( session, id) ;
if ( book == null ) {
session. log ( 'â ïļ Book not found: id=$ id ' , level: LogLevel . warning) ;
throw NotFoundException ( 'Book not found' ) ;
}
session . log ( 'â
Book lookup success: ${ book . title } ' ) ;
return book;
}
LogLevel Types #
Level Usage
LogLevel.debugDevelopment debugging
LogLevel.infoGeneral information (default)
LogLevel.warningWarnings
LogLevel.errorErrors
LogLevel.fatalFatal errors
Checklist #
When Implementing Repository #
During PR Review #