For now, don't pay attention to the rest, only the imports:
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser
We will use it later as a fixed field type in our documents.
This is not required by Couchbase, but is a good practice that will help you afterwards.
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser
In Couchbase, a bucket is a set of documents, that can be of different types.
They are generally all related to the same application.
The analogy in the relational database world would be a "database" (a specific database, not the database server).
The analogy in MongoDB would be a "collection".
In the code, a Bucket represents the main entrypoint of communication with the database.
This utility function will:
Connect to a Couchbase cluster (that might be a single machine).
Set defaults for timeouts.
Authenticate in the cluster.
Get a Bucket instance.
Set defaults for timeouts.
Return it.
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser
We will use this model in our path operation function, so, we don't include in it the hashed_password.
This will have the data that is actually stored in the database.
We don't create it as a subclass of Pydantic's BaseModel but as a subclass of our own User, because it will have all the attributes in User plus a couple more:
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser
Note
Notice that we have a hashed_password and a type field that will be stored in the database.
But it is not part of the general User model (the one we will return in the path operation).
Put the contents of the document in a UserInDB model.
By creating a function that is only dedicated to getting your user from a username (or any other parameter) independent of your path operation function, you can more easily reuse it in multiple parts and also add unit tests for it:
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser
As our code is calling Couchbase and we are not using the experimental Python await support, we should declare our function with normal def instead of async def.
Also, Couchbase recommends not using a single Bucket object in multiple "threads", so, we can just get the bucket directly and pass it to our utility functions:
fromtypingimportUnionfromcouchbaseimportLOCKMODE_WAITfromcouchbase.bucketimportBucketfromcouchbase.clusterimportCluster,PasswordAuthenticatorfromfastapiimportFastAPIfrompydanticimportBaseModelUSERPROFILE_DOC_TYPE="userprofile"defget_bucket():cluster=Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")authenticator=PasswordAuthenticator("username","password")cluster.authenticate(authenticator)bucket:Bucket=cluster.open_bucket("bucket_name",lockmode=LOCKMODE_WAIT)bucket.timeout=30bucket.n1ql_timeout=300returnbucketclassUser(BaseModel):username:stremail:Union[str,None]=Nonefull_name:Union[str,None]=Nonedisabled:Union[bool,None]=NoneclassUserInDB(User):type:str=USERPROFILE_DOC_TYPEhashed_password:strdefget_user(bucket:Bucket,username:str):doc_id=f"userprofile::{username}"result=bucket.get(doc_id,quiet=True)ifnotresult.value:returnNoneuser=UserInDB(**result.value)returnuser# FastAPI specific codeapp=FastAPI()@app.get("/users/{username}",response_model=User)defread_user(username:str):bucket=get_bucket()user=get_user(bucket=bucket,username=username)returnuser